import { LocalLanguage, Mutable } from "../../common/types";
import { objectAllocator } from "./impl";
import {
    Argument,
    ArrayDecl,
    ArrayTypeSignature,
    AssignmentExpression,
    BinaryExpression,
    BinaryOperatorKind,
    Binding,
    Block,
    Callee,
    CallExpr,
    CategoricalItemLiteral,
    CategoricalLiteral,
    ClassSignature,
    ComparisonPunctuationFlags,
    ComparisonRightValue,
    ConstantDeclaration,
    ConstSignature,
    Declaration,
    DeclarationKind,
    DeclareKeywordToken,
    DeclareModifierKind,
    DelcareKeywordKind,
    DoStat,
    EmptyExpr,
    EnumMemberSignature,
    EnumSignature,
    EventSection,
    EventSectionKind,
    Expression,
    ExprStat,
    FalseLiteral,
    FileKind,
    ForEachStat,
    ForStat,
    FunctionDeclaration,
    FunctionSignature,
    GlobalSQLVariablesSection,
    Identifier,
    IfStat,
    IndexExpr,
    IndexExprIndex,
    IndexExprObject,
    InheritLike,
    InputDataSourceSection,
    InterfaceSignature,
    IterationExpr,
    JobSection,
    JumpStatement,
    KeywordToken,
    KeywordTokenKind,
    LineLocation,
    Literal,
    LiteralKind,
    LiteralType,
    LocalDecls,
    LoggingSection,
    MemberExpr,
    MemberExpressionObject,
    MDMArealBase,
    MDMArealLike,
    MDMAreaName,
    MDMCategories,
    MDMCategory,
    MDMCategoryElementType,
    MDMCategoryElementTypeFlags,
    MDMCategoryExpression,
    MDMFactor,
    MDMKeycode,
    MDMCellStyle,
    MDMCellStyleProperties,
    MDMClass,
    MDMCollection,
    MDMControlStyle,
    MDMControlStyleProperties,
    MDMDbQuestions,
    MDMDbQuestionsProperties,
    MDMDeclaration,
    MDMDeclarationKind,
    MDMDeclarationValueLike,
    MDMDocument,
    MDMField,
    MDMFieldDefinitionHeader,
    MDMFieldDefinitionTail,
    MDMFieldFlags,
    MDMFontStyle,
    MDMFontStyleProperties,
    MDMHelperFields,
    MDMHierarchicalView,
    MDMKeyValuePair,
    MDMLabel,
    MDMLabeledObject,
    MDMLabelTypeFlags,
    MDMMultiplier,
    MDMOther,
    MDMPage,
    MDMProperties,
    MDMPropertiesLike,
    MDMProperty,
    MetadataSection,
    MDMSortOrder,
    MDMStyle,
    MDMStyleProperties,
    MDMSublist,
    MDMTemplate,
    MDMTemplates,
    MDMTokenKind,
    MDMUsageType,
    MDMUsageTypeFlags,
    MDMUserContextFlags,
    MDMValueRange,
    ModifierToken,
    ModuleSignature,
    MutableNodeArray,
    Node,
    NodeArray,
    NullLiteral,
    NumberLiteral,
    ObjectCollectionIterator,
    OptionFlag,
    OptionSettingKeyword,
    OptionStatement,
    OutputDataSourceSection,
    ParameterMacroArgument,
    ParameterMacroCallExpr,
    ParameterSignature,
    PrecedureCanExit,
    Preprocessor,
    PPDefineParameter,
    PPDefine,
    PPDefineStringizer,
    PPDefineTokenPaster,
    PPError,
    PPIfDef,
    PPIfNDef,
    PPIf,
    PPInclude,
    PPLine,
    PPPunctuation,
    PPPunctuationKind,
    PPUndef,
    PropertySignature,
    Punctuation,
    PunctuationKind,
    ReadonlyTextRange,
    ResumeStatement,
    RoutingSection,
    SectionKind,
    SectionStat,
    SelectCaseClause,
    SelectCaseComparison,
    SelectCaseCondition,
    SelectStat,
    Signature,
    SignatureKind,
    SourceFile,
    Statement,
    StatementKind,
    StringLiteral,
    SymbolTable,
    SyntaxKind,
    Token,
    TrueLiteral,
    UnaryExpr,
    UnaryOperatorKind,
    VariableDecl,
    WhileStat,
    WithStat
} from "./types";
import {
    isNode,
    isNodeArray,
    isPrecedureCanExitToken,
    isReadonlyArray,
    stringToEventSectionFlag,
    stringToLabelType,
    stringToMetadataElementTypeFlag,
    stringToUserContext
} from "./util";

export interface NodeFactory {
    createNode(flag: SyntaxKind): Node;
    updateNode(node: Mutable<Node>, flag: SyntaxKind, start?: number, end?: number): Node;
    updateNode<T extends Node>(node: Mutable<T>, flag?: SyntaxKind, start?: number, end?: number): T;
    createTokenNode<T extends SyntaxKind>(flag: T): Token<T>;
    createSourceFileNode(fileName: string, statements: readonly Statement[], eof: Token<SyntaxKind.eof>, fileFlag: FileKind): SourceFile;
    updateSourceFileNode(sourceFile: Mutable<SourceFile>, text?: string, flag?: FileKind): SourceFile;
    createNodeArray<T extends ReadonlyTextRange>(elements?: readonly T[]): NodeArray<T>;
    updateNodeArray<T extends ReadonlyTextRange>(nodeArray: NodeArray<T>, ...elements: readonly T[]): NodeArray<T>;
    // Literal
    createStringLiteral(text: string, literalType: LiteralType.multiLineString | LiteralType.singleLineString): StringLiteral;
    createNumberLiteral(value: string | number, literalType: LiteralType.number | LiteralType.octNumber | LiteralType.hexNumber): NumberLiteral;
    createCategoricalItemLiteral(upper?: NumberLiteral | Identifier, lower?: NumberLiteral | Identifier, exclude?: Punctuation<SyntaxKind.caret>, dotDot?: Token<SyntaxKind.dotDot>): CategoricalItemLiteral;
    createCategoricalItemLiteral(item?: NumberLiteral | Identifier, exclude?: Punctuation<SyntaxKind.caret>): CategoricalItemLiteral;
    createCategoricalLiteral(elements?: CategoricalItemLiteral[]): CategoricalLiteral;
    createTrueLiteral(): TrueLiteral;
    createFalseLiteral(): FalseLiteral;
    createNullLiteral(): NullLiteral;
    // Token
    createToken<T extends PunctuationKind>(flag: T): Punctuation<T>;
    createToken<T extends KeywordTokenKind>(flag: T): KeywordToken<T>;
    createToken<T extends DelcareKeywordKind>(flag: T): DeclareKeywordToken<T>;
    createToken<T extends DeclareModifierKind>(flag: T): ModifierToken<T>;
    createToken<T extends PPPunctuationKind>(flag: T): PPPunctuation<T>;
    createToken<T extends SyntaxKind.unknown | SyntaxKind.eof>(flag: T): Token<T>;
    createToken<T extends SyntaxKind>(flag: T): Token<T>;
    //
    createIdentifier(text?: string): Identifier;
    createLocation(name: Identifier): LineLocation;
    createBindingType(as: DeclareKeywordToken<SyntaxKind.asKeyword>, type: readonly Identifier[] | ArrayTypeSignature): Binding;
    createDeclaration<T extends DeclarationKind>(flag: T, identifier: Identifier): Declaration;
    createVariableDeclaration(name: Identifier, bindingType?: Binding): VariableDecl;
    createArrayDeclaration(name: Identifier, dimensions?: (NumberLiteral | EmptyExpr)[], bindingType?: Binding): ArrayDecl;
    updateArrayDeclaration(delcaration: Mutable<ArrayDecl>, dimensions?: (NumberLiteral | EmptyExpr)[]): ArrayDecl;
    createConstantDeclaration(name: Identifier, initialize: AssignmentExpression): ConstantDeclaration;
    createLocalDeclarations(...declarations: (VariableDecl | ArrayDecl)[]): LocalDecls;
    createParameterSignature(delcaration: VariableDecl | ArrayDecl, optional?: DeclareKeywordToken<SyntaxKind.optionalKeyword>, paramArray?: DeclareKeywordToken<SyntaxKind.paramArrayKeyword>, defaultExpr?: AssignmentExpression): ParameterSignature;
    createIterationExpression(lower: Expression | NumberLiteral, upper: Expression | NumberLiteral): IterationExpr;
    updateIterationExpression(expression: Mutable<IterationExpr>, lower: Expression | NumberLiteral | undefined, upper: Expression | NumberLiteral | undefined): IterationExpr;
    createFunctionDeclaration(name: Identifier, hasReturn: boolean, parameters?: NodeArray<ParameterSignature>, body?: Block): FunctionDeclaration;
    updateFunctionDeclaration(functionDeclaration: Mutable<FunctionDeclaration>, name: Identifier): FunctionDeclaration;
    updateFunctionDeclaration(functionDeclaration: Mutable<FunctionDeclaration>, hasReturn: boolean): FunctionDeclaration;
    updateFunctionDeclaration(functionDeclaration: Mutable<FunctionDeclaration>, parameters: readonly ParameterSignature[]): FunctionDeclaration;
    updateFunctionDeclaration(functionDeclaration: Mutable<FunctionDeclaration>, body?: Block): FunctionDeclaration;
    createConstantSignature(name: Identifier, bindingType: Binding): ConstSignature;
    createPropertySignature(name: Identifier, bindingType: Binding, modifiers?: readonly ModifierToken<DeclareModifierKind>[], parameters?: readonly ParameterSignature[]): PropertySignature;
    createFunctionSignature(name: Identifier, bindingType: Binding | undefined, parameters: readonly ParameterSignature[]): FunctionSignature;
    createInheritLike(names?: readonly Identifier[]): InheritLike;
    createInterfaceSignature(name: Identifier, inherits?: readonly (Identifier[])[], properties?: readonly PropertySignature[], methods?: readonly FunctionSignature[]): InterfaceSignature;
    updateInterfaceSignature(signature: Mutable<InterfaceSignature>, inherits?: readonly (Identifier[])[], properties?: readonly PropertySignature[], methods?: readonly FunctionSignature[]): InterfaceSignature;
    createClassSignature(name: Identifier, implementsInterfaces?: readonly (Identifier[])[], inherits?: readonly (Identifier[])[], properties?: readonly PropertySignature[], methods?: readonly FunctionSignature[], constants?: readonly ConstantDeclaration[]): ClassSignature;
    updateClassSignature(signature: Mutable<ClassSignature>, implementsInterfaces?: readonly (Identifier[])[], inherits?: readonly (Identifier[])[], properties?: readonly PropertySignature[], methods?: readonly FunctionSignature[], constants?: readonly ConstantDeclaration[]): ClassSignature;
    createEnumMemberSignature(name: Identifier, value: number, literal?: NumberLiteral): EnumMemberSignature;
    updateEnumMemberSignature(signature: Mutable<EnumMemberSignature>, value: number, literal?: NumberLiteral): EnumMemberSignature;
    createEnumSignature(name: Identifier, members: readonly EnumMemberSignature[]): EnumSignature;
    updateEnumSignature(signature: Mutable<EnumSignature>, ...members: readonly EnumMemberSignature[]): EnumSignature;
    createModuleSignature(name: Identifier, fullName?: readonly Identifier[], functions?: readonly FunctionSignature[], classes?: readonly ClassSignature[], interfaces?: readonly InterfaceSignature[], enums?: readonly EnumSignature[], constants?: readonly ConstSignature[]): ModuleSignature;
    updateModuleSignature(signature: Mutable<ModuleSignature>, functions?: readonly FunctionSignature[], classes?: readonly ClassSignature[], interfaces?: readonly InterfaceSignature[], enums?: readonly EnumSignature[], constants?: readonly ConstSignature[]): ModuleSignature;
    createArrayTypeSignature(name: Identifier, names: readonly Identifier[], rank: number): ArrayTypeSignature;
    updateArrayTypeSignature(signature: Mutable<ArrayTypeSignature>, name?: Identifier, names?: readonly Identifier[], rank?: number): ArrayTypeSignature;
    createUnaryExpression(operator: Token<UnaryOperatorKind>, expression: Expression): UnaryExpr;
    updateUnaryExpression(unary: Mutable<UnaryExpr>, operator: Token<UnaryOperatorKind>): UnaryExpr;
    updateUnaryExpression(unary: Mutable<UnaryExpr>, expression: Expression): UnaryExpr;
    createBinaryExpression(operator: Token<BinaryOperatorKind>, left: Expression, right: Expression): BinaryExpression;
    updateBinaryExpression(expression: Mutable<BinaryExpression>, operator: Token<BinaryOperatorKind>): BinaryExpression;
    updateBinaryExpression(expression: Mutable<BinaryExpression>, left: Expression, right: Expression): BinaryExpression;
    createAssignmentExpression(operator: Token<SyntaxKind.equal>, left: Expression, right: Expression, set?: KeywordToken<SyntaxKind.setKeyword>): AssignmentExpression;
    updateAssignmentExpression(expression: Mutable<AssignmentExpression>, left?: Expression, right?: Expression): AssignmentExpression;
    updateAssignmentExpression(expression: Mutable<AssignmentExpression>, set?: KeywordToken<SyntaxKind.setKeyword>): AssignmentExpression;
    createMemberExpression(object: MemberExpressionObject, member: Identifier): MemberExpr;
    updateMemberExpression(expression: Mutable<MemberExpr>, object?: MemberExpressionObject, member?: Identifier): MemberExpr;
    createObjectCollectionIterator(dotDot: Token<SyntaxKind.dotDot>, categories?: Identifier): ObjectCollectionIterator;
    updateObjectCollectionIterator(iterator: Mutable<ObjectCollectionIterator>, categories?: Identifier): ObjectCollectionIterator;
    createCallExpression(callee: Callee, args: Argument[]): CallExpr;
    updateCallExpression(callExpr: Mutable<CallExpr>, callee?: Callee, args?: readonly Argument[]): CallExpr;
    createParameterMacroCallExpression(callee: Identifier, args: readonly ParameterMacroArgument[]): ParameterMacroCallExpr;
    createEmptyExpression(): EmptyExpr;
    createStatement(flag: SyntaxKind): Statement;
    createExpressionStatement(expression: Expression): ExprStat;
    createBlockStatement(statements: readonly (Statement | Expression)[]): Block;
    updateBlockStatement(block: Mutable<Block>, ...statements: (Statement | Expression)[]): Block;
    createIfStatement(condition: Expression, then: Statement, singleLine?: boolean, elseStatement?: Statement): IfStat;
    updateIfStatement(statement: Mutable<IfStat>, condition: Expression): IfStat;
    updateIfStatement(statement: Mutable<IfStat>, then: Statement, elseStatement?: Statement): IfStat;
    updateIfStatement(statement: Mutable<IfStat>, singleLine: boolean): IfStat;
    createIndexExpression(object: IndexExprObject, index: IndexExprIndex): IndexExpr;
    updateIndexExpression(expression: Mutable<IndexExpr>, object?: IndexExprObject, index?: IndexExprIndex): IndexExpr;
    createForStatement(counter: Identifier, range: IterationExpr, step: NumberLiteral | undefined, statement?: Statement): ForStat;
    updateForStatement(statement: Mutable<ForStat>, counter: Identifier): ForStat;
    updateForStatement(statement: Mutable<ForStat>, range: IterationExpr): ForStat;
    updateForStatement(statement: Mutable<ForStat>, step: NumberLiteral): ForStat;
    updateForStatement(statement: Mutable<ForStat>, stat: Statement): ForStat;
    createForEachStatement(element: Identifier, group: Expression | Identifier): ForEachStat;
    updateForEachStatement(statement: Mutable<ForEachStat>, stat: Statement): ForEachStat;
    updateForEachStatement(statement: Mutable<ForEachStat>, element: Identifier | undefined, group: Expression | Identifier | undefined): ForEachStat;
    createWhileStatement(expression: Expression): WhileStat;
    updateWhileStatement(statement: Mutable<WhileStat>, expression: Expression): WhileStat;
    updateWhileStatement(statement: Mutable<WhileStat>, stat: Statement): WhileStat;
    createDoStatement(expression: Expression, preCondition: boolean, until?: KeywordToken<SyntaxKind.untilKeyword>): DoStat;
    updateDoStatement(statement: Mutable<DoStat>, expression: Expression): DoStat;
    updateDoStatement(statement: Mutable<DoStat>, preCondition: boolean): DoStat;
    updateDoStatement(statement: Mutable<DoStat>, until: KeywordToken<SyntaxKind.untilKeyword>): DoStat;
    updateDoStatement(statement: Mutable<DoStat>, stat: Statement): DoStat;
    createSelectCaseComparisonExpression(operator: Token<ComparisonPunctuationFlags>, right: ComparisonRightValue): SelectCaseComparison;
    updateSelectCaseComparisonExpression(expression: Mutable<SelectCaseComparison>, operator?: Token<ComparisonPunctuationFlags>, right?: ComparisonRightValue): SelectCaseComparison;
    createSelectCaseClause(caseElse: boolean, conditions?: readonly SelectCaseCondition[], statement?: Statement): SelectCaseClause;
    updateSelectCaseClause(clause: Mutable<SelectCaseClause>, statement: Statement): SelectCaseClause;
    updateSelectCaseClause(clause: Mutable<SelectCaseClause>, caseElse: boolean, ...conditions: SelectCaseCondition[]): SelectCaseClause
    createSelectStatement(condition: Expression, clauses: readonly SelectCaseClause[]): SelectStat;
    updateSelectStatement(statement: Mutable<SelectStat>, condition: Expression): SelectStat;
    updateSelectStatement(statement: Mutable<SelectStat>, clauses: readonly SelectCaseClause[]): SelectStat;
    createWithStatement(object: MemberExpressionObject, statement?: Statement): WithStat;
    updateWithStatement(statement: Mutable<WithStat>, object: MemberExpressionObject): WithStat;
    updateWithStatement(statement: Mutable<WithStat>, stat: Statement): WithStat;
    createSectionStatement(label: Identifier, statement?: Statement): SectionStat;
    updateSectionStatement(statement: Mutable<SectionStat>, label?: Identifier, stat?: Statement): SectionStat;
    createEventSection(name: Identifier, description?: StringLiteral, stat?: Block): EventSection;
    updateEventSection(statement: Mutable<EventSection>, name?: Identifier, description?: StringLiteral, stat?: Block): EventSection;
    createRoutingSection(name: Identifier, context: Identifier, stat?: Block): RoutingSection;
    updateRoutingSection(statement: Mutable<RoutingSection>, name?: Identifier, context?: Identifier, stat?: Block): RoutingSection;
    createJobSection(name: Identifier, description?: StringLiteral, tempDirectory?: AssignmentExpression): JobSection;
    updateJobSection(section: Mutable<JobSection>, name?: Identifier, description?: StringLiteral, tempDirectory?: AssignmentExpression): JobSection;
    createInputDataSourceSection(name: Identifier, description: StringLiteral | undefined, connectionString: AssignmentExpression, selectQuery?: AssignmentExpression, updateQuery?: AssignmentExpression, useInputAsOutput?: AssignmentExpression, joinKey?: AssignmentExpression, joinType?: AssignmentExpression, joinKeySorted?: AssignmentExpression): InputDataSourceSection;
    updateInputDataSourceSection(section: Mutable<InputDataSourceSection>, name?: Identifier, description?: StringLiteral, connectionString?: AssignmentExpression, selectQuery?: AssignmentExpression, updateQuery?: AssignmentExpression, useInputAsOutput?: AssignmentExpression, joinKey?: AssignmentExpression, joinType?: AssignmentExpression, joinKeySorted?: AssignmentExpression): InputDataSourceSection;
    createOutputDataSourceSection(name: Identifier, description?: StringLiteral, connectionString?: AssignmentExpression, metadataDataOutputName?: AssignmentExpression, updateQuery?: AssignmentExpression, tableOutputName?: AssignmentExpression, variableOrder?: AssignmentExpression): OutputDataSourceSection;
    updateOutputDataSourceSection(section: Mutable<OutputDataSourceSection>, name?: Identifier, description?: StringLiteral, connectionString?: AssignmentExpression, metadataDataOutputName?: AssignmentExpression, updateQuery?: AssignmentExpression, tableOutputName?: AssignmentExpression, variableOrder?: AssignmentExpression): OutputDataSourceSection;
    createGlobalSQLVariablesSection(name: Identifier, description: StringLiteral | undefined, connectionString: AssignmentExpression, selectQuery: AssignmentExpression): GlobalSQLVariablesSection;
    updateGlobalSQLVariablesSection(section: Mutable<GlobalSQLVariablesSection>, name?: Identifier, description?: StringLiteral, connectionString?: AssignmentExpression, selectQuery?: AssignmentExpression): GlobalSQLVariablesSection;
    createMetadataSection(name: Identifier, document?: MDMDocument, language?: Expression, context?: Expression, labelType?: Expression, dataSource?: Expression): MetadataSection;
    updateMetadataSection(section: Mutable<MetadataSection>, document?: MDMDocument, language?: Expression, context?: Expression, labelType?: Expression, dataSource?: Expression): MetadataSection;
    createLoggingSection(name: Identifier, description: StringLiteral | undefined, group: AssignmentExpression, path: AssignmentExpression, alias: AssignmentExpression, fileSize?: AssignmentExpression): LoggingSection;
    updateLoggingSection(section: Mutable<LoggingSection>, name?: Identifier, description?: StringLiteral, group?: AssignmentExpression, path?: AssignmentExpression, alias?: AssignmentExpression, fileSize?: AssignmentExpression): LoggingSection;
    createOptionStatement(option: KeywordToken<SyntaxKind.implicitKeyword | SyntaxKind.explicitKeyword | SyntaxKind.globalVariablesKeyword>): OptionStatement;
    createJumpStatement(goto: KeywordToken<SyntaxKind.gotoKeyword>, location: Identifier | NumberLiteral): JumpStatement;
    createJumpStatement(resume: ResumeStatement): JumpStatement;
    createJumpStatement(exit: KeywordToken<SyntaxKind.exitKeyword>, precedure?: KeywordToken<PrecedureCanExit>): JumpStatement;
    createResumeStatement(resume: KeywordToken<SyntaxKind.resumeKeyword>, next: KeywordToken<SyntaxKind.nextKeyword>): ResumeStatement;
    createPreprocessorDefineParameter(name: Identifier, isStringizer?: boolean, isTokenPaster?: boolean): PPDefineParameter;
    updatePreprocessorDefineParameter(parameter: Mutable<PPDefineParameter>, isStringizer?: boolean, isTokenPaster?: boolean): PPDefineParameter;
    createPreprocessorDefineStatement(name: Identifier, parameters: PPDefineParameter[] | undefined, definition: readonly Node[] | undefined): PPDefine;
    createPreprocessorUndefStatement(name: Identifier): PPUndef;
    createPreprocessorIncludeStatement(path: StringLiteral | Expression, resolvedPath: string, targetFile?: SourceFile): PPInclude;
    createPreprocessorIfStatement(condition: Expression, then: Statement, elseStatement?: Statement): PPIf;
    createPreprocessorIfDefStatement(macroName: Identifier, then: Statement, elseStatement?: Statement): PPIfDef;
    createPreprocessorIfNDefStatement(macroName: Identifier, then: Statement, elseStatement?: Statement): PPIfNDef;
    createPreprocessorErrorStatement(text: string): PPError;
    createPreprocessorLineStatement(sequence: NumberLiteral, location?: StringLiteral): PPLine;
    createPreprocessorDefineStringizer(name: Identifier): PPDefineStringizer;
    createPreprocessorDefineTokenPaster(identifiers: readonly Identifier[]): PPDefineTokenPaster;
    // metadata
    createMetadataAreaName(areaName: Identifier, language?: Expression, userContext?: Expression, labelType?: Expression): MDMAreaName;
    updateMetadataAreaName(areaName: Mutable<MDMAreaName>, language?: Expression, userContext?: Expression, labelType?: Expression): MDMAreaName;
    createMetadataProperty(name: Identifier, value: Literal | Identifier): MDMProperty;
    updateMetadataProperty(property: Mutable<MDMProperty>, name?: Identifier, value?: Literal | Identifier): MDMProperty;
    createMetadataTemplate(name: Identifier, value: Literal | Identifier): MDMTemplate;
    createMetadataControlStyle(name: Identifier, properties: readonly [key: keyof(MDMControlStyleProperties), value: MDMProperty][]): MDMControlStyle;
    createMetadataFontStyle(name: Identifier, properties: readonly [key: keyof(MDMFontStyleProperties), value: MDMProperty][]): MDMFontStyle;
    createMetadataCellStyle(name: Identifier, properties: readonly [key: keyof(MDMCellStyleProperties), value: MDMProperty][]): MDMCellStyle;
    createMetadataStyle(name: Identifier, properties?: MDMStyleProperties, control?: MDMControlStyle, cell?: MDMCellStyle, font?: MDMFontStyle): MDMStyle;
    createMetadataProperties(name: Identifier, properties?: readonly MDMProperty[], areaName?: Identifier): MDMProperties;
    createMetadataTemplates(name: Identifier, templates?: readonly MDMTemplate[], areaName?: Identifier): MDMTemplates;
    createMetadataLabel(label: StringLiteral | Token<SyntaxKind.minus>, areaName?: Identifier, areaNames?: readonly MDMLabel[]): MDMLabel;
    updateMetadataLabel(labelObject: Mutable<MDMLabel>, label: StringLiteral | Token<SyntaxKind.minus>): MDMLabel;
    createMetadataUsageType(name: Identifier, typeValue: StringLiteral, typeFlag: MDMUsageTypeFlags): MDMUsageType;
    createMetadataHelperFields(name: Identifier, fields: readonly MDMField[]): MDMHelperFields;
    createMetadataCategoryExpression(name: Identifier, expression: StringLiteral, arg?: Token<SyntaxKind.mdmDeriveElementsKeyword | SyntaxKind.mdmNoDeriveElementsKeyword>): MDMCategoryExpression;
    createMetadataCategoryElementType(name: Identifier, elementType: Identifier | StringLiteral): MDMCategoryElementType;
    createMetadataCategoryKeycode(name: Identifier, value: StringLiteral): MDMKeycode;
    createMetadataCategoryFactor(name: Identifier, value: NumberLiteral | TrueLiteral | FalseLiteral | StringLiteral | NullLiteral): MDMFactor;
    createMetadataCategorySublist(name: Identifier, label: MDMLabel | undefined, sortOrder?: MDMSortOrder): MDMSublist;
    createMetadataCategoryMultiplier(name: Identifier, field?: StringLiteral | MDMField): MDMMultiplier;
    createMetadataCategoryOther(name: Identifier, field?: StringLiteral | MDMField): MDMOther;
    createMetadataCategory(name: Identifier, label?: MDMLabel, categories?: MDMCategory[]): MDMCategory;
    createMetadataCategory(name: Identifier, label?: MDMLabel, list?: Identifier, sublist?: MDMSublist): MDMCategory;
    createMetadataCategories(categories: MDMCategory[], sortOrder?: MDMSortOrder, namespace?: Token<SyntaxKind.mdmNamespaceKeyword>): MDMCategories;
    createMetadataDeclarationValueLike<F extends MDMDeclarationKind, V>(flag: F, name: Identifier, value: V): MDMDeclarationValueLike<F, V>;
    createMetadataValueRange(lowerBoundary?: NumberLiteral | StringLiteral, upperBoundary?: NumberLiteral | StringLiteral, step?: NumberLiteral, exclude?: Token<SyntaxKind.caret>, dotDot?: Token<SyntaxKind.dotDot>): MDMValueRange;
    createMetadataField(
        name: Identifier,
        label: MDMLabel | undefined,
        valueRange?: readonly MDMValueRange[],
        header?: MDMFieldDefinitionHeader,
        tail?: MDMFieldDefinitionTail,
        categories?: MDMCategory[],
        rowOrColumn?: Token<SyntaxKind.mdmRowKeyword | SyntaxKind.mdmColumnKeyword>,
        expand?: Token<SyntaxKind.mdmExpandKeyword>,
        grid?: Token<SyntaxKind.mdmGridKeyword>,
    ): MDMField;
    createMetadataClass(
        name: Identifier,
        label?: MDMLabel,
        types?: readonly MDMField[],
        fields?: readonly MDMField[],
        pages?: readonly MDMField[]): MDMClass;
    createMetadataDatabaseQuestions(
        baseField: MDMField,
        properties: MDMDbQuestionsProperties,
        dbType?: MDMFieldFlags,
        noExpand?: Token<SyntaxKind.mdmNoExpandKeyword>,
    ): MDMDbQuestions;
    createMetadataHierarchicalView(field: MDMField): MDMHierarchicalView;
    createMetadataPage(baseField: MDMField, questionNames: readonly Identifier[]): MDMPage;
    createMetadataDocument(
        language: LocalLanguage,
        context: MDMUserContextFlags,
        label: MDMLabelTypeFlags,
        types?: readonly MDMField[],
        fields?: readonly MDMField[],
        hdata?: MDMHierarchicalView,
        lcl?: readonly MDMAreaName[],
        preprocessor?: readonly Preprocessor[],
    ): MDMDocument;

}

export function createNodeFactory(): NodeFactory {

    let nodeConstructor: new (flag: SyntaxKind, start: number, end: number) => Node;
    let identifierConstructor: new (flag?: SyntaxKind.identifier, start?: number, end?: number) => Identifier;
    let metadataArealLikeConstructor: new <T extends MDMArealBase>(flag: MDMTokenKind, start: number, end: number) => MDMArealLike<T>;
    let metadataPropertiesLikeConstructor: new <T extends MDMKeyValuePair, U extends MDMDeclaration>(flag: U["kind"], start: number, end: number) => MDMPropertiesLike<T, U>;
    let metadataStyleConstructor: new (flag: SyntaxKind.mdmStyle, start: number, end: number, name: Identifier) => MDMStyle;
    let metadataCollectionConstructor: new <T extends MDMLabeledObject>(flag: MDMTokenKind, start: number, end: number) => MDMCollection<T>;
    let sourceFileConstructor: new (flag: SyntaxKind.sourceFile, start: number, end: number) => SourceFile;
    let symbolTableConstructor: new () => SymbolTable;

    return {
        createNode,
        updateNode,
        createTokenNode,
        createSourceFileNode,
        updateSourceFileNode,
        createNodeArray,
        updateNodeArray,
        createStringLiteral,
        createNumberLiteral,
        createCategoricalItemLiteral,
        createCategoricalLiteral,
        createTrueLiteral,
        createFalseLiteral,
        createNullLiteral,
        createIdentifier,
        createToken,
        createDeclaration,
        createBindingType,
        createLocation,
        createVariableDeclaration,
        createArrayDeclaration,
        updateArrayDeclaration,
        createConstantDeclaration,
        createLocalDeclarations,
        createParameterSignature,
        createIterationExpression,
        updateIterationExpression,
        createParameterMacroCallExpression,
        createFunctionDeclaration,
        updateFunctionDeclaration,
        createConstantSignature,
        createPropertySignature,
        createFunctionSignature,
        createInheritLike,
        createInterfaceSignature,
        updateInterfaceSignature,
        createClassSignature,
        updateClassSignature,
        createEnumMemberSignature,
        updateEnumMemberSignature,
        createEnumSignature,
        updateEnumSignature,
        createModuleSignature,
        updateModuleSignature,
        createArrayTypeSignature,
        updateArrayTypeSignature,
        createUnaryExpression,
        updateUnaryExpression,
        createBinaryExpression,
        updateBinaryExpression,
        createAssignmentExpression,
        updateAssignmentExpression,
        createMemberExpression,
        updateMemberExpression,
        createObjectCollectionIterator,
        updateObjectCollectionIterator,
        createCallExpression,
        updateCallExpression,
        createEmptyExpression,
        createStatement,
        createExpressionStatement,
        createBlockStatement,
        updateBlockStatement,
        createIfStatement,
        updateIfStatement,
        createIndexExpression,
        updateIndexExpression,
        createForStatement,
        updateForStatement,
        createForEachStatement,
        updateForEachStatement,
        createWhileStatement,
        updateWhileStatement,
        createDoStatement,
        updateDoStatement,
        createSelectCaseComparisonExpression,
        updateSelectCaseComparisonExpression,
        createSelectCaseClause,
        updateSelectCaseClause,
        createSelectStatement,
        updateSelectStatement,
        createWithStatement,
        updateWithStatement,
        createSectionStatement,
        updateSectionStatement,
        createEventSection,
        updateEventSection,
        createRoutingSection,
        updateRoutingSection,
        createJobSection,
        updateJobSection,
        createInputDataSourceSection,
        updateInputDataSourceSection,
        createOutputDataSourceSection,
        updateOutputDataSourceSection,
        createGlobalSQLVariablesSection,
        updateGlobalSQLVariablesSection,
        createMetadataSection,
        updateMetadataSection,
        createLoggingSection,
        updateLoggingSection,
        createOptionStatement,
        createJumpStatement,
        createResumeStatement,
        createPreprocessorDefineParameter,
        updatePreprocessorDefineParameter,
        createPreprocessorDefineStatement,
        createPreprocessorUndefStatement,
        createPreprocessorIncludeStatement,
        createPreprocessorIfStatement,
        createPreprocessorIfDefStatement,
        createPreprocessorIfNDefStatement,
        createPreprocessorErrorStatement,
        createPreprocessorLineStatement,
        createPreprocessorDefineStringizer,
        createPreprocessorDefineTokenPaster,
        // metadata
        createMetadataAreaName,
        updateMetadataAreaName,
        createMetadataProperty,
        updateMetadataProperty,
        createMetadataTemplate,
        createMetadataControlStyle,
        createMetadataFontStyle,
        createMetadataCellStyle,
        createMetadataStyle,
        createMetadataProperties,
        createMetadataTemplates,
        createMetadataLabel,
        updateMetadataLabel,
        createMetadataDeclarationValueLike,
        createMetadataUsageType,
        createMetadataHelperFields,
        createMetadataCategorySublist: createMetadataSublist,
        createMetadataCategoryExpression,
        createMetadataCategoryElementType,
        createMetadataCategoryKeycode,
        createMetadataCategoryFactor,
        createMetadataCategoryMultiplier,
        createMetadataCategoryOther,
        createMetadataCategory,
        createMetadataCategories,
        createMetadataValueRange,
        createMetadataClass,
        createMetadataField,
        createMetadataDatabaseQuestions,
        createMetadataHierarchicalView,
        createMetadataPage,
        createMetadataDocument,
    };

    function createSymbolTable(): SymbolTable {
        return new (symbolTableConstructor || (symbolTableConstructor = objectAllocator.getSymbolTableConstructor()))();
    }

    function createNode(flag: SyntaxKind): Node {
        return new (nodeConstructor || (nodeConstructor = objectAllocator.getNodeConstructor()))(flag, -1, -1) as Mutable<Node>;
    }

    function createBaseNode<T extends Node>(flag: T["kind"]): Mutable<T> {
        return createNode(flag) as Mutable<T>;
    }

    function updateNode(node: Mutable<Node>, flag: SyntaxKind, start?: number, end?: number): Node;
    function updateNode<T extends Node>(node: Mutable<T>, flag?: SyntaxKind, start?: number, end?: number): T;
    function updateNode(node: Mutable<Node>, flag?: SyntaxKind, start?: number, end?: number): Node {
        if (!node) {
            return node;
        }

        if (flag !== undefined) {
            node.kind = flag;
        }

        if (start !== undefined) {
            node.start = start;
        }

        if (end !== undefined) {
            node.end = end;
        }

        return node;
    }

    function createTokenNode<T extends SyntaxKind>(flag: T): Token<T> {
        return createBaseNode(flag) as Mutable<Token<T>>;
    }

    function createNodeArray<T extends ReadonlyTextRange>(elements?: readonly T[]): NodeArray<T> {
        if (!elements) {
            elements = [];
        } else if (isNodeArray(elements)) {
            const array = elements.slice() as MutableNodeArray<T>;
            array.start = elements.start;
            array.end = elements.end;
            return array;
        }

        const length = elements.length;
        const array = (length >= 1 && length <= 4 ? elements.slice() : elements) as MutableNodeArray<T>;
        array.start = -1;
        array.end = -1;
        for (const item of array) {
            if (array.start === -1 || item.start < array.start) {
                array.start = item.start;
            }
            if (item.end > array.end) {
                array.end = item.end;
            }
        }
        return array;
    }

    function updateNodeArray<T extends ReadonlyTextRange>(nodeArray: NodeArray<T>, ...elements: (T | undefined)[]): NodeArray<T> {
        const target = nodeArray as MutableNodeArray<T>;
        for (const n of elements) {
            if (!n) {
                continue;
            }
            target.push(n);
            if (nodeArray.start === -1 || (n.start < nodeArray.start && n.start > -1)) {
                target.start = n.start;
            }
            if (n.end > nodeArray.end) {
                target.end = n.end;
            }
        }
        return target;
    }

    function createSourceFileNode(fileName: string, statements: readonly Statement[], eof: Token<SyntaxKind.eof>, fileFlag: FileKind): SourceFile {
        const node = new (sourceFileConstructor || (sourceFileConstructor = objectAllocator.getSourceFileConstructor()))(SyntaxKind.sourceFile, -1, -1) as Mutable<SourceFile>;
        node.fileName = fileName;
        node.fileKind = fileFlag;
        node.statements = createNodeArray(statements);
        node.asChildren(...statements);
        node.eof = eof;
        return node;
    }

    function updateSourceFileNode(sourceFile: Mutable<SourceFile>, text?: string, flag?: FileKind): SourceFile {
        sourceFile.text = text || sourceFile.text;
        sourceFile.fileKind = flag || sourceFile.fileKind;
        return sourceFile;
    }

    function createToken<T extends PunctuationKind>(flag: T): Punctuation<T>;
    function createToken<T extends KeywordTokenKind>(flag: T): KeywordToken<T>;
    function createToken<T extends DelcareKeywordKind>(flag: T): DeclareKeywordToken<T>;
    function createToken<T extends DeclareModifierKind>(flag: T): ModifierToken<T>;
    function createToken<T extends PPPunctuationKind>(flag: T): PPPunctuation<T>;
    function createToken<T extends SyntaxKind>(flag: T) {
        const token = createBaseNode(flag) as Mutable<Token<T>>;
        token.text = "";
        switch (flag) {
            case SyntaxKind.andKeyword:
            case SyntaxKind.caseKeyword:
            case SyntaxKind.constKeyword:
            case SyntaxKind.dimKeyword:
            case SyntaxKind.doKeyword:
            case SyntaxKind.eachKeyword:
            case SyntaxKind.elseKeyword:
            case SyntaxKind.elseifKeyword:
            case SyntaxKind.endKeyword:
            case SyntaxKind.errorKeyword:
            case SyntaxKind.exitKeyword:
            case SyntaxKind.explicitKeyword:
            case SyntaxKind.falseKeyword:
            case SyntaxKind.forKeyword:
            case SyntaxKind.functionKeyword:
            case SyntaxKind.globalVariablesKeyword:
            case SyntaxKind.gotoKeyword:
            case SyntaxKind.ifKeyword:
            case SyntaxKind.implicitKeyword:
            case SyntaxKind.inKeyword:
            case SyntaxKind.isKeyword:
            case SyntaxKind.likeKeyword:
            case SyntaxKind.loopKeyword:
            case SyntaxKind.mod:
            case SyntaxKind.nextKeyword:
            case SyntaxKind.notKeyword:
            case SyntaxKind.nullKeyword:
            case SyntaxKind.onKeyword:
            case SyntaxKind.optionKeyword:
            case SyntaxKind.orKeyword:
            case SyntaxKind.paper:
            case SyntaxKind.resumeKeyword:
            case SyntaxKind.sectionKeyword:
            case SyntaxKind.selectKeyword:
            case SyntaxKind.setKeyword:
            case SyntaxKind.stepKeyword:
            case SyntaxKind.subKeyword:
            case SyntaxKind.thenKeyword:
            case SyntaxKind.toKeyword:
            case SyntaxKind.trueKeyword:
            case SyntaxKind.untilKeyword:
            case SyntaxKind.whileKeyword:
            case SyntaxKind.withKeyword:
            case SyntaxKind.xorKeyword:
                token.keyword = flag;
                break;

            case SyntaxKind.openBracket:
            case SyntaxKind.closeBracket:
            case SyntaxKind.openCurly:
            case SyntaxKind.closeCurly:
            case SyntaxKind.openParenthesis:
            case SyntaxKind.closeParenthesis:
            case SyntaxKind.dot:
            case SyntaxKind.dotDot:
            case SyntaxKind.caret:
            case SyntaxKind.comma:
            case SyntaxKind.colon:
                token.punctuation = flag;
                break;

            case SyntaxKind.asKeyword:
            case SyntaxKind.moduleKeyword:
            case SyntaxKind.interfaceKeyword:
            case SyntaxKind.classKeyword:
            case SyntaxKind.implementsKeyword:
            case SyntaxKind.paramArrayKeyword:
            case SyntaxKind.propertyKeyword:
            case SyntaxKind.readonlyKeyword:
            case SyntaxKind.writeonlyKeyword:
            case SyntaxKind.optionalKeyword:
                token.delcareKeyword = flag;
                break;

            case SyntaxKind.readonlyKeyword:
            case SyntaxKind.writeonlyKeyword:
            case SyntaxKind.defaultKeyword:
            case SyntaxKind.optionalKeyword:
                token.declareModifier = flag;
                break;

            case SyntaxKind.ampersandAmpersand:
            case SyntaxKind.barbar:
            case SyntaxKind.exclamation:
            case SyntaxKind.exclamationEqual:
                token.preprocessorPunctuation = flag;
                break;

            }

        return token;
    }

    function createExpression<T extends Expression>(flag: T["kind"]): Mutable<T> {
        const node = createBaseNode(flag) as Mutable<T>;
        node._expressionBrand = undefined;
        return node;
    }

    function createStringLiteral(text: string, literalType: LiteralType.multiLineString | LiteralType.singleLineString): StringLiteral {
        const literal = createExpression(SyntaxKind.stringLiteral) as Mutable<StringLiteral>;
        literal.literalType = literalType;
        literal.text = text;
        return literal;
    }

    function createNumberLiteral(value: string | number, literalType: LiteralType.number | LiteralType.octNumber | LiteralType.hexNumber): NumberLiteral {
        const literal = createExpression(SyntaxKind.numberLiteral) as Mutable<NumberLiteral>;
        literal.literalType = literalType;
        if (typeof value === 'number') {
            literal.value = value;
        } else {
            let radix = literalType === LiteralType.number ? 10 : (literalType === LiteralType.octNumber ? 8 : 16);
            literal.value = parseInt(value, radix);
        }
        return literal;
    }

    function createCategoricalItemLiteral(upper?: NumberLiteral | Identifier, lowerOrExclude?: NumberLiteral | Identifier | Punctuation<SyntaxKind.caret>, exclude?: Punctuation<SyntaxKind.caret>, dotDot?: Token<SyntaxKind.dotDot>): CategoricalItemLiteral {
        const literal = createExpression(SyntaxKind.categoricalItemLiteral) as Mutable<CategoricalItemLiteral>;
        literal.upper = upper;
        literal.dotDot = dotDot;
        if (lowerOrExclude && lowerOrExclude.isPunctuation(SyntaxKind.caret)) {
            literal.exclude = lowerOrExclude;
            literal.isRange = false;
        } else {
            literal.lower = lowerOrExclude;
            literal.exclude = exclude;
            literal.isRange = true;
        }
        return literal;
    }

    function createCategoricalLiteral(elements?: CategoricalItemLiteral[]): CategoricalLiteral {
        const categorical = createExpression(SyntaxKind.categoricalLiteral) as Mutable<CategoricalLiteral>;
        if (elements) {
            categorical.categories = createNodeArray(elements);
            categorical.asChildren(...elements);
        }
        return categorical;
    }

    function createSimpleLiteral<T extends LiteralKind, TNode extends Literal>(flag: T, text: string, literalType: LiteralType): Mutable<TNode> {
        const literal = createExpression(flag) as Mutable<TNode>;
        literal.text = text;
        literal.literalType = literalType;
        literal._expressionBrand = undefined;
        return literal;
    }

    function createTrueLiteral(): TrueLiteral {
        return createSimpleLiteral<SyntaxKind.trueLiteral, TrueLiteral>(SyntaxKind.trueLiteral, "true", LiteralType.true);
    }

    function createFalseLiteral(): FalseLiteral {
        return createSimpleLiteral<SyntaxKind.falseLiteral, FalseLiteral>(SyntaxKind.falseLiteral, "false", LiteralType.false);
    }

    function createNullLiteral(): NullLiteral {
        return createSimpleLiteral<SyntaxKind.nullLiteral, NullLiteral>(SyntaxKind.nullLiteral, "null", LiteralType.null);
    }

    function createIdentifier(text?: string): Identifier {
        const identifier = new (identifierConstructor || (identifierConstructor = objectAllocator.getIdentifierConstructor()))() as Mutable<Identifier>;
        identifier.text = text || "";
        return identifier;
    }

    function createLocation(name: Identifier): LineLocation {
        const location = createDeclaration(SyntaxKind.location, name) as Mutable<LineLocation>;
        location._locationBrand = undefined;
        location._statementBrand = undefined;
        location.asChildren(name);
        return location;
    }

    function createDeclaration<T extends DeclarationKind>(flag: T, identifier: Identifier): Declaration {
        const declaration = createBaseNode(flag) as Mutable<Declaration>;
        declaration.name = identifier;
        declaration._statementBrand = undefined;
        declaration._declarationBrand = undefined;
        declaration.asChildren(identifier);
        return declaration;
    }

    function createBindingType(as: DeclareKeywordToken<SyntaxKind.asKeyword>, type: readonly Identifier[] | ArrayTypeSignature): Binding {
        const bindingType = createBaseNode(SyntaxKind.bindingType) as Mutable<Binding>;
        bindingType.as = as;
        bindingType.asChildren(as);
        if (isReadonlyArray(type)) {
            bindingType.type = createNodeArray(type);
            bindingType.asChildren(...type);
        }
        else {
            bindingType.type = type;
            bindingType.asChildren(type);
        }
        return bindingType;
    }

    function createSignature<T extends SignatureKind>(flag: T, identifier: Identifier): Signature<T> {
        const signature = createBaseNode(flag) as Mutable<Signature<T>>;
        signature.name = identifier;
        signature._statementBrand = undefined;
        signature.asChildren(identifier);
        return signature;
    }

    function createVariableDeclaration(name: Identifier, bindingType?: Binding): VariableDecl {
        const variable = createDeclaration<SyntaxKind.varDecl>(SyntaxKind.varDecl, name) as Mutable<VariableDecl>;
        variable.bindingType = bindingType;
        variable.asChildren(bindingType);
        return variable;
    }

    function createArrayDeclaration(name: Identifier, dimensions?: (NumberLiteral | EmptyExpr)[], bindingType?: Binding): ArrayDecl {
        const array = createDeclaration(SyntaxKind.arrayDecl, name) as Mutable<ArrayDecl>;
        array.dimensions = createNodeArray(dimensions);
        array.rank = array.dimensions.length;
        array.bindingType = bindingType;
        if (dimensions) {
            array.asChildren(...dimensions);
        }
        if (bindingType) {
            array.asChildren(bindingType);
        }
        return array;
    }

    function updateArrayDeclaration(delcaration: Mutable<ArrayDecl>, dimensions?: (NumberLiteral | EmptyExpr)[]): ArrayDecl {
        if (dimensions) {
            delcaration.dimensions = createNodeArray(dimensions);
            delcaration.rank = dimensions.length;
            delcaration.removeChild(child => child.isNumberLiteral() || child.isExpression<EmptyExpr>(SyntaxKind.emptyExpr));
            delcaration.asChildren(...dimensions);
        }
        return delcaration;
    }

    function createConstantDeclaration(name: Identifier, initialize: AssignmentExpression): ConstantDeclaration {
        const declaration = createDeclaration(SyntaxKind.constDecl, name) as Mutable<ConstantDeclaration>;
        declaration.initialize = initialize;
        declaration.asChildren(initialize);
        return declaration;
    }

    function createLocalDeclarations(...declarations: (VariableDecl | ArrayDecl)[]): LocalDecls {
        const locals = createStatement(SyntaxKind.localDecls) as Mutable<LocalDecls>;
        locals.declarations = createNodeArray(declarations);
        locals.asChildren(...declarations);
        return locals;
    }

    function createConstantSignature(name: Identifier, bindingType: Binding): ConstSignature {
        const signature = createSignature(SyntaxKind.constSignature, name) as Mutable<ConstSignature>;
        signature.bindingType = bindingType;
        signature.asChildren(bindingType);
        return signature;
    }

    function createParameterSignature(declaration: VariableDecl | ArrayDecl, optional?: DeclareKeywordToken<SyntaxKind.optionalKeyword>, paramArray?: DeclareKeywordToken<SyntaxKind.paramArrayKeyword>, defaultExpr?: AssignmentExpression): ParameterSignature {
        const param = createSignature(SyntaxKind.parameterSignature, declaration.name) as Mutable<ParameterSignature>;
        param.optional = optional;
        param.paramArray = paramArray;
        param.default = defaultExpr;
        param.declaration = declaration;
        param.asChildren(optional, paramArray, defaultExpr, declaration);
        return param;
    }

    function createIterationExpression(lower: Expression | NumberLiteral, upper: Expression | NumberLiteral): IterationExpr {
        const range = createBaseNode(SyntaxKind.iteration) as Mutable<IterationExpr>;
        range.lower = lower;
        range.upper = upper;
        range.asChildren(lower, upper);
        return range;
    }

    function updateIterationExpression(expression: Mutable<IterationExpr>, lower: Expression | NumberLiteral | undefined, upper: Expression | NumberLiteral | undefined): IterationExpr {
        expression.removceAllChildren();
        if (lower) {
            expression.lower = lower;
        }
        if (upper) {
            expression.upper = upper;
        }
        expression.asChildren(expression.lower, expression.upper);
        return expression;
    }

    function createFunctionDeclaration(name: Identifier, hasReturn: boolean, parameters?: NodeArray<ParameterSignature>, body?: Block): FunctionDeclaration {
        const functionDeclaration = createDeclaration(SyntaxKind.funcDecl, name) as Mutable<FunctionDeclaration>;
        functionDeclaration.hasReturn = hasReturn;
        functionDeclaration.parameters = parameters ?? createNodeArray([]);
        functionDeclaration.body = body ?? createBlockStatement([]);
        functionDeclaration.locals = createSymbolTable();
        functionDeclaration.asChildren(name);
        if (parameters) {
            functionDeclaration.asChildren(...parameters);
        }
        functionDeclaration.asChildren(body);
        return functionDeclaration;
    }

    function updateFunctionDeclaration(functionDeclaration: Mutable<FunctionDeclaration>, name: Identifier): FunctionDeclaration;
    function updateFunctionDeclaration(functionDeclaration: Mutable<FunctionDeclaration>, hasReturn: boolean): FunctionDeclaration;
    function updateFunctionDeclaration(functionDeclaration: Mutable<FunctionDeclaration>, parameters: ParameterSignature[]): FunctionDeclaration;
    function updateFunctionDeclaration(functionDeclaration: Mutable<FunctionDeclaration>, body?: Block): FunctionDeclaration;
    function updateFunctionDeclaration(functionDeclaration: Mutable<FunctionDeclaration>, parameter: Identifier | boolean | ParameterSignature[] | Block | undefined): FunctionDeclaration {
        if (typeof parameter === 'boolean') {
            functionDeclaration.hasReturn = parameter;
        } else if (Array.isArray(parameter)) {
            functionDeclaration.removeChild(child => child.isSignature<ParameterSignature>(SyntaxKind.parameterSignature));
            functionDeclaration.parameters = createNodeArray(parameter);
            functionDeclaration.asChildren(...parameter);
        } else if (parameter) {
            if (parameter.isIdentifier()) {
                functionDeclaration.name = parameter;
            } else {
                functionDeclaration.body = parameter;
            }
            functionDeclaration.asChildren(parameter);
        }
        return functionDeclaration;
    }

    function createPropertySignature(name: Identifier, bindingType: Binding, modifiers?: readonly ModifierToken<DeclareModifierKind>[], parameters?: readonly ParameterSignature[]): PropertySignature {
        const propertySignature = createNode(SyntaxKind.propertySignature) as Mutable<PropertySignature>;
        propertySignature.name = name;
        propertySignature.bindingType = bindingType;
        propertySignature.modifiers = createNodeArray(modifiers);
        propertySignature.parameters = createNodeArray(parameters);
        propertySignature.asChildren(name, bindingType);
        if (modifiers) {
            propertySignature.asChildren(...modifiers);
        }
        if (parameters) {
            propertySignature.asChildren(...parameters);
        }
        return propertySignature;
    }

    function createFunctionSignature(name: Identifier, bindingType: Binding | undefined, parameters: readonly ParameterSignature[]): FunctionSignature {
        const signature = createSignature(SyntaxKind.functionSignature, name) as Mutable<FunctionSignature>;
        signature.bindingType = bindingType;
        signature.parameters = createNodeArray(parameters);
        signature.asChildren(bindingType, ...parameters);
        return signature;
    }

    function createEnumMemberSignature(name: Identifier, value: number, literal?: NumberLiteral): EnumMemberSignature {
        const enumMember = createSignature(SyntaxKind.enumMemberSignature, name) as Mutable<EnumMemberSignature>;
        enumMember.value = value;
        enumMember.literal = literal;
        enumMember.asChildren(name, literal);
        return enumMember;
    }

    function updateEnumMemberSignature(signature: Mutable<EnumMemberSignature>, value: number, literal?: NumberLiteral): EnumMemberSignature {
        signature.value = value;
        if (literal) {
            signature.removeChild(child => child.isNumberLiteral());
            signature.literal = literal;
            signature.asChildren(literal);
        }
        return signature;
    }

    function createEnumSignature(name: Identifier, members: readonly EnumMemberSignature[]): EnumSignature {
        const enumSignature = createSignature(SyntaxKind.enumSignature, name) as Mutable<EnumSignature>;
        enumSignature.members = createNodeArray(members);
        enumSignature.asChildren(name, ...members);
        return enumSignature;
    }

    function updateEnumSignature(signature: Mutable<EnumSignature>, ...members: EnumMemberSignature[]): EnumSignature {
        signature.members = updateNodeArray(signature.members,...members);
        signature.removceAllChildren();
        signature.asChildren(...members);
        return signature;
    }

    function createInheritLike(names?: readonly Identifier[]): InheritLike {
        const node = createNode(SyntaxKind.inheritLike) as Mutable<InheritLike>;
        node.fullName = createNodeArray(names);
        if (names && names.length > 0) {
            node.start = node.fullName.start;
            node.end = node.fullName.end;
        }
        return node;
    }

    function createInheritLikes(nodes?: readonly (Identifier[])[]): NodeArray<InheritLike> {
        const inherits: InheritLike[] = [];
        if (nodes) {
            for (const node of nodes) {
                inherits.push(createInheritLike(node));
            }
        }
        return createNodeArray(inherits);
    }

    function createInterfaceSignature(name: Identifier, inherits?: readonly (Identifier[])[], properties?: readonly PropertySignature[], methods?: readonly FunctionSignature[]): InterfaceSignature {
        const signature = createSignature(SyntaxKind.interfaceSignature, name) as Mutable<InterfaceSignature>;
        signature.inherits = createInheritLikes(inherits);
        signature.properties = createNodeArray(properties);
        signature.methods = createNodeArray(methods);
        if (inherits) {
            for (const inherit of inherits) {
                signature.asChildren(...inherit);
            }
        }
        if (properties) {
            signature.asChildren(...properties);
        }
        if (methods) {
            signature.asChildren(...methods);
        }
        return signature;
    }

    function updateInterfaceSignature(signature: Mutable<InterfaceSignature>, inherits?: readonly (Identifier[])[], properties?: readonly PropertySignature[], methods?: readonly FunctionSignature[]): InterfaceSignature {
        signature.removceAllChildren();
        if (inherits) {
            signature.inherits = createInheritLikes(inherits);
        }
        if (properties) {
            signature.properties = updateNodeArray(signature.properties, ...properties);
        }
        if (methods) {
            signature.methods = updateNodeArray(signature.methods, ...methods);
        }
        if (signature.inherits) {
            for (const inherit of signature.inherits) {
                signature.asChildren(inherit);
            }
        }
        signature.asChildren(...signature.properties, ...signature.methods);
        return signature;
    }

    function createClassSignature(name: Identifier, implementsInterfaces?: readonly (Identifier[])[], inherits?: readonly (Identifier[])[], properties?: readonly PropertySignature[], methods?: readonly FunctionSignature[], constants?: readonly ConstantDeclaration[]): ClassSignature {
        const signature = createSignature(SyntaxKind.classSignature, name) as Mutable<ClassSignature>;
        signature.implements = createInheritLikes(implementsInterfaces);
        signature.inherits = createInheritLikes(inherits);
        signature.properties = createNodeArray(properties);
        signature.methods = createNodeArray(methods);
        signature.constants = createNodeArray(constants);
        if (implementsInterfaces) {
            for (const impl of implementsInterfaces) {
                signature.asChildren(...impl);
            }
        }
        if (inherits) {
            for (const inh of inherits) {
                signature.asChildren(...inh);
            }
        }
        if (properties) {
            signature.asChildren(...properties);
        }
        if (methods) {
            signature.asChildren(...methods);
        }
        if (constants) {
            signature.asChildren(...constants);
        }
        return signature;
    }

    function updateClassSignature(signature: Mutable<ClassSignature>, implementsInterfaces?: readonly (Identifier[])[], inherits?: readonly (Identifier[])[], properties?: readonly PropertySignature[], methods?: readonly FunctionSignature[], constants?: readonly ConstantDeclaration[]): ClassSignature {
        signature.removceAllChildren();
        if (implementsInterfaces) {
            signature.implements = createInheritLikes(implementsInterfaces);
        }
        if (inherits) {
            signature.inherits = createInheritLikes(inherits);
        }
        if (properties) {
            signature.properties = updateNodeArray(signature.properties, ...properties);
        }
        if (methods) {
            signature.methods = updateNodeArray(signature.methods, ...methods);
        }
        if (constants) {
            signature.constants = updateNodeArray(signature.constants, ...constants);
        }
        if (signature.implements) {
            for (const imp of signature.implements) {
                signature.asChildren(imp);
            }
        }
        if (signature.inherits) {
            for (const inh of signature.inherits) {
                signature.asChildren(inh);
            }
        }
        signature.asChildren(...signature.properties, ...signature.methods, ...signature.constants);
        return signature;
    }

    function createModuleSignature(name: Identifier, fullName?: readonly Identifier[], functions?: readonly FunctionSignature[], classes?: readonly ClassSignature[], interfaces?: readonly InterfaceSignature[], enums?: readonly EnumSignature[], constants?: readonly ConstSignature[]): ModuleSignature {
        const signature = createSignature(SyntaxKind.moduleSignature, name) as Mutable<ModuleSignature>;
        signature.fullName = createNodeArray(fullName);
        signature.functions = createNodeArray(functions);
        signature.classes = createNodeArray(classes);
        signature.interfaces = createNodeArray(interfaces);
        signature.enums = createNodeArray(enums);
        signature.constants = createNodeArray(constants);
        signature.locals = createSymbolTable();
        if (fullName && fullName.length > 1) {
            for (let i = 0; i < fullName.length - 1; i++) {
                signature.asChildren(fullName[i]);
            }
        }
        if (functions) {
            signature.asChildren(...functions);
        }
        if (classes) {
            signature.asChildren(...classes);
        }
        if (interfaces) {
            signature.asChildren(...interfaces);
        }
        if (enums) {
            signature.asChildren(...enums);
        }
        if (constants) {
            signature.asChildren(...constants);
        }
        return signature;
    }

    function updateModuleSignature(signature: Mutable<ModuleSignature>, functions?: readonly FunctionSignature[], classes?: readonly ClassSignature[], interfaces?: readonly InterfaceSignature[], enums?: readonly EnumSignature[], constants?: readonly ConstSignature[]): ModuleSignature {
        signature.removceAllChildren();
        signature.asChildren(...signature.fullName);
        if (functions) {
            signature.functions = createNodeArray(functions);
        }
        if (classes) {
            signature.classes = createNodeArray(classes);
        }
        if (interfaces) {
            signature.interfaces = createNodeArray(interfaces);
        }
        if (enums) {
            signature.enums = createNodeArray(enums);
        }
        if (constants) {
            signature.constants = createNodeArray(constants);
        }
        signature.asChildren(...signature.functions, ...signature.classes, ...signature.interfaces, ...signature.enums, ...signature.constants);
        return signature;
    }

    function createArrayTypeSignature(name: Identifier, names: readonly Identifier[], rank: number): ArrayTypeSignature {
        const signature = createSignature(SyntaxKind.arrayTypeSignature, name) as Mutable<ArrayTypeSignature>;
        signature.fullName = createNodeArray(names);
        signature.rank = rank;
        if (names.length > 1) {
            signature.removceAllChildren();
            signature.asChildren(...names);
        }
        return signature;
    }

    function updateArrayTypeSignature(signature: Mutable<ArrayTypeSignature>, name?: Identifier, names?: readonly Identifier[], rank?: number): ArrayTypeSignature {
        signature.removceAllChildren();
        if (name) {
            signature.name = name;
            if (!names || names.length === 1) {
                signature.asChildren(name);
            }
        }
        if (names) {
            signature.fullName = createNodeArray(names);
            if (names && names.length > 1) {
                signature.asChildren(...names);
            }
        }
        if (rank !== undefined && rank > 0) {
            signature.rank = rank;
        }
        return signature;
    }

    function createUnaryExpression(operator: Token<UnaryOperatorKind>, expression: Expression): UnaryExpr {
        const unary = createExpression<UnaryExpr>(SyntaxKind.unaryExpr);
        unary.operator = operator;
        unary.expression = expression;
        unary.asChildren(operator, expression);
        return unary;
    }

    function updateUnaryExpression(unary: Mutable<UnaryExpr>, operator: Token<UnaryOperatorKind>): UnaryExpr;
    function updateUnaryExpression(unary: Mutable<UnaryExpr>, expression: Expression): UnaryExpr;
    function updateUnaryExpression(unary: Mutable<UnaryExpr>, parameter: Token<UnaryOperatorKind> | Expression): UnaryExpr {
        unary.removceAllChildren();
        if (parameter.isUnaryOperatorToken()) {
            unary.operator = parameter;
        } else {
            unary.expression = parameter;
        }
        unary.asChildren(unary.operator, unary.expression);
        return unary;
    }

    function createBinaryExpression(operator: Token<BinaryOperatorKind>, left: Expression, right: Expression): BinaryExpression {
        const binary = createExpression<BinaryExpression>(SyntaxKind.binaryExpr);
        binary.operator = operator;
        binary.left = left;
        binary.right = right;
        binary.asChildren(left, operator, right);
        return binary;
    }

    function updateBinaryExpression(expression: Mutable<BinaryExpression>, operator: Token<BinaryOperatorKind>): BinaryExpression;
    function updateBinaryExpression(expression: Mutable<BinaryExpression>, left: Expression, right: Expression): BinaryExpression;
    function updateBinaryExpression(expression: Mutable<BinaryExpression>, param1: Token<BinaryOperatorKind> | Expression, param2?: Expression): BinaryExpression {
        expression.removceAllChildren();
        if (param1.isBinaryOperatorToken()) {
            expression.operator = param1;
        } else {
            expression.left = param1;
        }
        if (param2) {
            expression.right = param2;
        }
        expression.asChildren(expression.left, expression.operator, expression.right);
        return expression;
    }

    function createAssignmentExpression(operator: Token<SyntaxKind.equal>, left: Expression, right: Expression, set?: KeywordToken<SyntaxKind.setKeyword>): AssignmentExpression {
        const assignment = createBinaryExpression(operator, left, right) as Mutable<AssignmentExpression>;
        assignment.set = set;
        assignment._assignmentBrand = undefined;
        assignment.asChildren(set);
        return assignment;
    }

    function updateAssignmentExpression(expression: Mutable<AssignmentExpression>, left?: Expression, right?: Expression): AssignmentExpression;
    function updateAssignmentExpression(expression: Mutable<AssignmentExpression>, set?: KeywordToken<SyntaxKind.setKeyword>): AssignmentExpression;
    function updateAssignmentExpression(expression: Mutable<AssignmentExpression>, param1?: Expression | KeywordToken<SyntaxKind.setKeyword>, param2?: Expression): AssignmentExpression {
        expression.removceAllChildren();
        if (param1) {
            if (param1.isToken(SyntaxKind.setKeyword)) {
                expression.set = param1;
            } else {
                expression.left = param1;
            }
        }
        if (param2) {
            expression.right = param2;
        }
        expression.asChildren(expression.set, expression.left, expression.operator, expression.right);
        return expression;
    }

    function createMemberExpression(object: MemberExpressionObject, member: Identifier): MemberExpr {
        const memberExpr = createExpression<MemberExpr>(SyntaxKind.memberExpr);
        memberExpr.object = object;
        memberExpr.member = member;
        memberExpr.asChildren(object, member);
        return memberExpr;
    }

    function updateMemberExpression(expression: Mutable<MemberExpr>, object?: MemberExpressionObject, member?: Identifier): MemberExpr {
        expression.removceAllChildren();
        if (object) {
            expression.object = object;
        }
        if (member) {
            expression.member = member;
        }
        expression.asChildren(expression.object, expression.member);
        return expression;
    }

    function createObjectCollectionIterator(dotDot: Token<SyntaxKind.dotDot>, categories?: Identifier): ObjectCollectionIterator {
        const iterator = createExpression<ObjectCollectionIterator>(SyntaxKind.objectCollectionIterator);
        iterator.dotDot = dotDot;
        iterator.categories = categories;
        iterator.asChildren(dotDot, categories);
        return iterator;
    }

    function updateObjectCollectionIterator(iterator: Mutable<ObjectCollectionIterator>, categories?: Identifier): ObjectCollectionIterator {
        iterator.removceAllChildren();
        if (categories) {
            iterator.categories = categories;
        }
        iterator.asChildren(iterator.dotDot, iterator.categories);
        return iterator;
    }

    function createIndexExpression(object: IndexExprObject, index: IndexExprIndex): IndexExpr {
        const indexExpr = createExpression<IndexExpr>(SyntaxKind.indexExpr);
        indexExpr.object = object;
        indexExpr.index = index;
        indexExpr.asChildren(object, index);
        return indexExpr;
    }

    function updateIndexExpression(expression: Mutable<IndexExpr>, object?: IndexExprObject, index?: IndexExprIndex): IndexExpr {
        expression.removceAllChildren();
        if (object) {
            expression.object = object;
        }
        if (index) {
            expression.index = index;
        }
        expression.asChildren(expression.object, expression.index);
        return expression;
    }

    function createCallExpression(callee: Callee, args: readonly Argument[]): CallExpr {
        const callExpr = createExpression<CallExpr>(SyntaxKind.callExpr);
        callExpr.callee = callee;
        if (args) {
            callExpr.arguments = createNodeArray(args);
        }
        callExpr.callByDot = callee.kind !== SyntaxKind.identifier;
        callExpr.asChildren(callee, ...args);
        return callExpr;
    }

    function updateCallExpression(callExpr: Mutable<CallExpr>, callee?: Callee, args?: readonly Argument[]): CallExpr {
        callExpr.removceAllChildren();
        if (callee) {
            callExpr.callee = callee;
        }
        if (args) {
            callExpr.arguments = updateNodeArray(callExpr.arguments, ...args);
        }
        callExpr.asChildren(callExpr.callee, ...callExpr.arguments);
        return callExpr;
    }

    function createParameterMacroCallExpression(callee: Identifier, args: readonly ParameterMacroArgument[]): ParameterMacroCallExpr {
        const callExpr = createExpression<ParameterMacroCallExpr>(SyntaxKind.parameterMacroCallExpr);
        callExpr.callee = callee;
        callExpr.arguments = createNodeArray(args);
        return callExpr;
    }

    function createEmptyExpression(): EmptyExpr {
        return createExpression<EmptyExpr>(SyntaxKind.emptyExpr);
    }

    function createStatement(flag: StatementKind): Statement {
        const node = createNode(flag) as Mutable<Statement>;
        node._statementBrand = true;
        return node;
    }

    function createExpressionStatement(expression: Expression): ExprStat {
        const statement = createStatement(SyntaxKind.exprStat) as Mutable<ExprStat>;
        statement.expression = expression;
        statement.asChildren(expression);
        return statement;
    }

    function createBlockStatement(statements: readonly (Statement | Expression)[]): Block {
        const block = createStatement(SyntaxKind.block) as Mutable<Block>;
        block.statements = createNodeArray(statements);
        block.locations = {
            nodes: [],
            symbols: new Map(),
        };
        block.asChildren(...statements);
        return block;
    }

    function updateBlockStatement(block: Mutable<Block>, ...statements: (Statement | Expression)[]): Block {
        block.removceAllChildren();
        if (statements) {
            block.statements = updateNodeArray(block.statements, ...statements);
        }
        block.asChildren(...block.statements);
        return block;
    }

    function createIfStatement(condition: Expression, then: Statement, singleLine?: boolean, elseStatement?: Statement): IfStat {
        const statement = createStatement(SyntaxKind.ifStat) as Mutable<IfStat>;
        statement.condition = condition;
        statement.then = then;
        statement.singleLine = singleLine === undefined ? true : singleLine;
        statement.else = elseStatement;
        statement.asChildren(condition, then, elseStatement);
        return statement;
    }

    function updateIfStatement(statement: Mutable<IfStat>, condition: Expression): IfStat;
    function updateIfStatement(statement: Mutable<IfStat>, then: Statement, elseStatement?: Statement): IfStat;
    function updateIfStatement(statement: Mutable<IfStat>, singleLine: boolean): IfStat;
    function updateIfStatement(statement: Mutable<IfStat>, param1: Expression | Statement | boolean, param2?: Statement): IfStat {
        statement.removceAllChildren();
        if (typeof param1 === 'boolean') {
            statement.singleLine = param1;
        } else if (param1.isExpression()) {
            statement.condition = param1;
        } else {
            statement.then = param1;
        }

        if (param2) {
            statement.else = param2;
        }
        statement.asChildren(statement.condition, statement.then, statement.else);
        return statement;
    }

    function createForStatement(counter: Identifier, range: IterationExpr, step: NumberLiteral | undefined, stat?: Statement): ForStat {
        const statement = createStatement(SyntaxKind.forStat) as Mutable<ForStat>;
        statement.counter = counter;
        statement.range = range;
        statement.step = step;
        if (stat) {
            statement.statement = stat;
        }
        statement.asChildren(counter, range, step, stat);
        return statement;
    }

    function updateForStatement(statement: Mutable<ForStat>, counter: Identifier): ForStat;
    function updateForStatement(statement: Mutable<ForStat>, range: IterationExpr): ForStat;
    function updateForStatement(statement: Mutable<ForStat>, step: NumberLiteral): ForStat;
    function updateForStatement(statement: Mutable<ForStat>, stat: Statement): ForStat;
    function updateForStatement(statement: Mutable<ForStat>, param: Identifier | IterationExpr | NumberLiteral | Statement): ForStat {
        statement.removceAllChildren();
        if (param.isExpression<IterationExpr>(SyntaxKind.iteration)) {
            statement.range = param;
        }
        else if (param.isNumberLiteral()) {
            statement.step = param;
        }
        else if (param.isStatement()) {
            statement.statement = param;
        }
        else {
            statement.counter = param;
        }
        statement.asChildren(statement.counter, statement.range, statement.step, statement.statement);
        return statement;
    }

    function createForEachStatement(element: Identifier, group: Expression | Identifier, stat?: Statement): ForEachStat {
        const statement = createStatement(SyntaxKind.forEachStat) as Mutable<ForEachStat>;
        statement.element = element;
        statement.group = group;
        if (stat) {
            statement.statement = stat;
        }
        statement.asChildren(element, group, stat);
        return statement;
    }

    function updateForEachStatement(statement: Mutable<ForEachStat>, stat: Statement): ForEachStat;
    function updateForEachStatement(statement: Mutable<ForEachStat>, element: Identifier | undefined, group: Expression | Identifier | undefined): ForEachStat;
    function updateForEachStatement(statement: Mutable<ForEachStat>, arg: Identifier | Statement | undefined, group?: Expression | Identifier): ForEachStat {
        statement.removceAllChildren();
        if (arg && arg.isStatement()) {
            statement.statement = arg;
        }
        else {
            if (arg) {
                statement.element = arg;
            }
            if (group) {
                statement.group = group;
            }
        }
        statement.asChildren(statement.element, statement.group, statement.statement);
        return statement;
    }

    function createWhileStatement(expression: Expression): WhileStat {
        const statement = createStatement(SyntaxKind.whileStat) as Mutable<WhileStat>;
        statement.expression = expression;
        statement.asChildren(expression);
        return statement;
    }

    function updateWhileStatement(statement: Mutable<WhileStat>, expression: Expression): WhileStat;
    function updateWhileStatement(statement: Mutable<WhileStat>, stat: Statement): WhileStat;
    function updateWhileStatement(statement: Mutable<WhileStat>, arg: Expression | Statement): WhileStat {
        statement.removceAllChildren();
        if (arg.isStatement()) {
            statement.statement = arg;
        }
        else {
            statement.expression = arg;
        }
        statement.asChildren(statement.expression, statement.statement);
        return statement;
    }

    function createDoStatement(expression: Expression, preCondition: boolean, until?: KeywordToken<SyntaxKind.untilKeyword>): DoStat {
        const statement = createStatement(SyntaxKind.doStat) as Mutable<DoStat>;
        statement.expression = expression;
        statement.preCondition = preCondition;
        statement.until = until;
        statement.asChildren(expression, until);
        return statement;
    }

    function updateDoStatement(statement: Mutable<DoStat>, expression: Expression): DoStat;
    function updateDoStatement(statement: Mutable<DoStat>, preCondition: boolean): DoStat;
    function updateDoStatement(statement: Mutable<DoStat>, until: KeywordToken<SyntaxKind.untilKeyword>): DoStat;
    function updateDoStatement(statement: Mutable<DoStat>, stat: Statement): DoStat;
    function updateDoStatement(statement: Mutable<DoStat>, param: Expression | boolean | KeywordToken<SyntaxKind.untilKeyword> | Statement): DoStat {
        statement.removceAllChildren();
        if (typeof param === 'boolean') {
            statement.preCondition = param;
        } else if (param.isExpression()) {
            statement.expression = param;
        } else if (param.isStatement()) {
            statement.statement = param;
        } else {
            statement.until = param;
        }
        statement.asChildren(statement.expression, statement.until, statement.statement);
        return statement;
    }

    function createSelectCaseComparisonExpression(operator: Token<ComparisonPunctuationFlags>, right: ComparisonRightValue): SelectCaseComparison {
        const comparison = createNode(SyntaxKind.selectCaseComparison) as Mutable<SelectCaseComparison>;
        comparison.operator = operator;
        comparison.right = right;
        comparison.asChildren(operator, right);
        return comparison;
    }

    function updateSelectCaseComparisonExpression(expression: Mutable<SelectCaseComparison>, operator?: Token<ComparisonPunctuationFlags>, right?: ComparisonRightValue): SelectCaseComparison {
        expression.removceAllChildren();
        if (operator) {
            expression.operator = operator;
        }
        if (right) {
            expression.right = right;
        }
        expression.asChildren(expression.operator, expression.right);
        return expression;
    }

    function createSelectCaseClause(caseElse: boolean, conditions?: readonly SelectCaseCondition[], statement?: Statement): SelectCaseClause {
        const clause = createNode(SyntaxKind.selectCaseClause) as Mutable<SelectCaseClause>;
        clause.caseElse = caseElse;
        clause.conditions = createNodeArray(conditions);
        if (conditions) {
            clause.asChildren(...conditions);
        }
        if (statement) {
            clause.statement = statement;
            clause.asChildren(statement);
        }
        return clause;
    }

    function updateSelectCaseClause(clause: Mutable<SelectCaseClause>, statement: Statement): SelectCaseClause;
    function updateSelectCaseClause(clause: Mutable<SelectCaseClause>, caseElse: boolean, ...conditions: SelectCaseCondition[]): SelectCaseClause;
    function updateSelectCaseClause(clause: Mutable<SelectCaseClause>, arg: boolean | Statement, ...conditions: SelectCaseCondition[]): SelectCaseClause {
        clause.removceAllChildren();
        if (typeof arg === "boolean") {
            clause.caseElse = arg;
            clause.conditions = createNodeArray(conditions);
        }
        else {
            clause.statement = arg;
        }
        clause.asChildren(...clause.conditions, clause.statement);
        return clause;
    }

    function createSelectStatement(condition: Expression, clauses: readonly SelectCaseClause[]): SelectStat {
        const statement = createStatement(SyntaxKind.selectStat) as Mutable<SelectStat>;
        statement.test = condition;
        statement.cases = createNodeArray(clauses);
        statement.asChildren(condition, ...clauses);
        return statement;
    }

    function updateSelectStatement(statement: Mutable<SelectStat>, condition: Expression): SelectStat;
    function updateSelectStatement(statement: Mutable<SelectStat>, clauses: readonly SelectCaseClause[]): SelectStat;
    function updateSelectStatement(statement: Mutable<SelectStat>, param1: Expression | readonly SelectCaseClause[]): SelectStat {
        statement.removceAllChildren();
        if (isReadonlyArray(param1)) {
            statement.cases = createNodeArray(param1);
        } else {
            statement.test = param1;
        }
        statement.asChildren(statement.test, ...statement.cases);
        return statement;
    }

    function createWithStatement(object: MemberExpressionObject, stat?: Statement): WithStat {
        const statement = createStatement(SyntaxKind.withStat) as Mutable<WithStat>;
        statement.object = object;
        if (stat) {
            statement.statement = statement;
        }
        statement.asChildren(object, stat);
        return statement;
    }

    function updateWithStatement(statement: Mutable<WithStat>, object: MemberExpressionObject): WithStat;
    function updateWithStatement(statement: Mutable<WithStat>, stat: Statement): WithStat;
    function updateWithStatement(statement: Mutable<WithStat>, arg: MemberExpressionObject | Statement): WithStat {
        statement.removceAllChildren();
        if (arg.isStatement()) {
            statement.statement = arg;
        }
        else {
            statement.object = arg;
        }
        statement.asChildren(statement.object, statement.statement);
        return statement;
    }

    function createSectionStatement(name: Identifier, statement?: Statement): SectionStat {
        const section = createDeclaration(SyntaxKind.sectionStat, name) as Mutable<SectionStat>;
        if (statement) {
            section.statement = statement;
        }
        section.asChildren(name, statement);
        return section;
    }

    function updateSectionStatement(statement: Mutable<SectionStat>, label?: Identifier, stat?: Statement): SectionStat {
        statement.removceAllChildren();
        if (label) {
            statement.name = label;
        }
        if (stat) {
            statement.statement = stat;
        }
        statement.asChildren(statement.name, statement.statement);
        return statement;
    }

    function createEventSection(name: Identifier, description?: StringLiteral, stat?: Block): EventSection {
        const statement = createDeclaration(SyntaxKind.eventSection, name) as Mutable<EventSection>;
        statement.description = description;
        statement.eventSectionFlag = stringToEventSectionFlag(name.text) || EventSectionKind.none;
        statement.sectionFlag = SectionKind.event;
        if (stat) {
            statement.statement = stat;
        }
        statement.functions = createNodeArray();
        statement.includes = [];
        statement.locals = createSymbolTable();
        statement.asChildren(name, description, stat);
        return statement;
    }

    function updateEventSection(statement: Mutable<EventSection>, name?: Identifier, description?: StringLiteral, stat?: Block): EventSection {
        statement.removceAllChildren();
        if (name) {
            statement.name = name;
            statement.eventSectionFlag = stringToEventSectionFlag(name.text) || EventSectionKind.none;
        }
        if (description) {
            statement.description = description;
        }
        if (stat) {
            statement.statement = stat;
        }
        statement.asChildren(statement.name, statement.description, statement.statement);
        return statement;
    }

    function createRoutingSection(name: Identifier, context: Identifier, stat?: Block): RoutingSection {
        const statement = createDeclaration(SyntaxKind.routingSection, name) as Mutable<RoutingSection>;
        statement.context = context;
        if (stat) {
            statement.statement = stat;
        }
        statement.functions = createNodeArray();
        statement.locals = createSymbolTable();
        statement.asChildren(name, context, stat);
        return statement;
    }

    function updateRoutingSection(statement: Mutable<RoutingSection>, name?: Identifier, context?: Identifier, stat?: Block): RoutingSection {
        statement.removceAllChildren();
        if (name) {
            statement.name = name;
        }
        if (context) {
            statement.context = context;
        }
        if (stat) {
            statement.statement = stat;
        }
        statement.asChildren(statement.name, statement.context, statement.statement);
        return statement;
    }

    function createJobSection(name: Identifier, description?: StringLiteral, tempDirectory?: AssignmentExpression): JobSection {
        const section = createDeclaration(SyntaxKind.jobSection, name) as Mutable<JobSection>;
        section.description = description;
        section.tempDirectory = tempDirectory;
        section.asChildren(name, description, tempDirectory);
        return section;
    }

    function updateJobSection(section: Mutable<JobSection>, name?: Identifier, description?: StringLiteral, tempDirectory?: AssignmentExpression): JobSection {
        section.removceAllChildren();
        if (name) {
            section.name = name;
        }
        if (description) {
            section.description = description;
        }
        if (tempDirectory) {
            section.tempDirectory = tempDirectory;
        }
        section.asChildren(section.name, section.description, section.tempDirectory);
        return section;
    }

    function createInputDataSourceSection(name: Identifier, description: StringLiteral | undefined, connectionString: AssignmentExpression, selectQuery?: AssignmentExpression, updateQuery?: AssignmentExpression, useInputAsOutput?: AssignmentExpression, joinKey?: AssignmentExpression, joinType?: AssignmentExpression, joinKeySorted?: AssignmentExpression): InputDataSourceSection {
        const section = createDeclaration(SyntaxKind.inputDataSourceSection, name) as Mutable<InputDataSourceSection>;
        section.description = description;
        section.connectionString = connectionString;
        section.selectQuery = selectQuery;
        section.updateQuery = updateQuery;
        section.useInputAsOutput = useInputAsOutput;
        section.joinKey = joinKey;
        section.joinType = joinType;
        section.joinKeySorted = joinKeySorted;
        section.asChildren(name, description, connectionString, selectQuery, updateQuery, useInputAsOutput, joinKey, joinType, joinKeySorted);
        return section;
    }

    function updateInputDataSourceSection(section: Mutable<InputDataSourceSection>, name?: Identifier, description?: StringLiteral, connectionString?: AssignmentExpression, selectQuery?: AssignmentExpression, updateQuery?: AssignmentExpression, useInputAsOutput?: AssignmentExpression, joinKey?: AssignmentExpression, joinType?: AssignmentExpression, joinKeySorted?: AssignmentExpression): InputDataSourceSection {
        if (name) {
            section.name = name;
        }
        if (description) {
            section.description = description;
        }
        if (connectionString) {
            section.connectionString = connectionString;
        }
        if (selectQuery) {
            section.selectQuery = selectQuery;
        }
        if (updateQuery) {
            section.updateQuery = updateQuery;
        }
        if (useInputAsOutput) {
            section.useInputAsOutput = useInputAsOutput;
        }
        if (joinKey) {
            section.joinKey = joinKey;
        }
        if (joinType) {
            section.joinType = joinType;
        }
        if (joinKeySorted) {
            section.joinKeySorted = joinKeySorted;
        }
        section.removceAllChildren();
        section.asChildren(section.name,
            section.description,
            section.connectionString,
            section.selectQuery,
            section.updateQuery,
            section.useInputAsOutput,
            section.joinKey,
            section.joinType,
            section.joinKeySorted);
        return section;
    }

    function createOutputDataSourceSection(name: Identifier, description?: StringLiteral, connectionString?: AssignmentExpression, metadataDataOutputName?: AssignmentExpression, updateQuery?: AssignmentExpression, tableOutputName?: AssignmentExpression, variableOrder?: AssignmentExpression): OutputDataSourceSection {
        const section = createDeclaration(SyntaxKind.outputDataSourceSection, name) as Mutable<OutputDataSourceSection>;
        section.description = description;
        section.connectionString = connectionString;
        section.metadataDataOutputName = metadataDataOutputName;
        section.updateQuery = updateQuery;
        section.tableOutputName = tableOutputName;
        section.variableOrder = variableOrder;
        return section;
    }

    function updateOutputDataSourceSection(section: Mutable<OutputDataSourceSection>, name?: Identifier, description?: StringLiteral, connectionString?: AssignmentExpression, metadataDataOutputName?: AssignmentExpression, updateQuery?: AssignmentExpression, tableOutputName?: AssignmentExpression, variableOrder?: AssignmentExpression): OutputDataSourceSection {
        if (name) {
            section.name = name;
        }
        if (description) {
            section.description = description;
        }
        if (connectionString) {
            section.connectionString = connectionString;
        }
        if (metadataDataOutputName) {
            section.metadataDataOutputName = metadataDataOutputName;
        }
        if (updateQuery) {
            section.updateQuery = updateQuery;
        }
        if (tableOutputName) {
            section.tableOutputName = tableOutputName;
        }
        if (variableOrder) {
            section.variableOrder = variableOrder;
        }
        section.removceAllChildren();
        section.asChildren(section.name, section.description, section.connectionString, section.metadataDataOutputName, section.updateQuery, section.tableOutputName, section.variableOrder);
        return section;
    }

    function createGlobalSQLVariablesSection(name: Identifier, description: StringLiteral | undefined, connectionString: AssignmentExpression, selectQuery: AssignmentExpression): GlobalSQLVariablesSection {
        const section = createDeclaration(SyntaxKind.globalSQLVariablesSection, name) as Mutable<GlobalSQLVariablesSection>;
        section.description = description;
        section.connectionString = connectionString;
        section.selectQuery = selectQuery;
        section.asChildren(name, description, connectionString, selectQuery);
        return section;
    }

    function updateGlobalSQLVariablesSection(section: Mutable<GlobalSQLVariablesSection>, name?: Identifier, description?: StringLiteral, connectionString?: AssignmentExpression, selectQuery?: AssignmentExpression): GlobalSQLVariablesSection {
        if (name) {
            section.name = name;
        }
        if (description) {
            section.description = description;
        }
        if (connectionString) {
            section.connectionString = connectionString;
        }
        if (selectQuery) {
            section.selectQuery = selectQuery;
        }
        section.removceAllChildren();
        section.asChildren(section.name, section.description, section.connectionString, section.selectQuery);
        return section;
    }

    function createMetadataSection(name: Identifier, document: MDMDocument, language?: Expression, context?: Expression, labelType?: Expression, dataSource?: Expression): MetadataSection {
        const section = createDeclaration(SyntaxKind.metadataSection, name) as Mutable<MetadataSection>;
        section.language = language;
        section.document = document;
        section.context = context;
        section.labelType = labelType;
        section.dataSource = dataSource;
        section.includes = [];
        section.asChildren(language, document, context, labelType, dataSource);
        return section;
    }

    function updateMetadataSection(section: Mutable<MetadataSection>, document?: MDMDocument, language?: Expression, context?: Expression, labelType?: Expression, dataSource?: Expression): MetadataSection {
        if (language) {
            section.language = language;
        }
        if (document) {
            section.document = document;
        }
        if (context) {
            section.context = context;
        }
        if (labelType) {
            section.labelType = labelType;
        }
        if (dataSource) {
            section.dataSource = dataSource;
        }
        section.removceAllChildren();
        section.asChildren(section.language, section.document, section.context, section.labelType, section.dataSource);
        return section;
    }

    function createLoggingSection(name: Identifier, description: StringLiteral | undefined, group: AssignmentExpression, path: AssignmentExpression, alias: AssignmentExpression, fileSize?: AssignmentExpression): LoggingSection {
        const section = createDeclaration(SyntaxKind.loggingSection, name) as Mutable<LoggingSection>;
        section.description = description;
        section.group = group;
        section.path = path;
        section.alias = alias;
        section.fileSize = fileSize;
        section.asChildren(name, description, group, path, alias, fileSize);
        return section;
    }

    function updateLoggingSection(section: Mutable<LoggingSection>, name?: Identifier, description?: StringLiteral, group?: AssignmentExpression, path?: AssignmentExpression, alias?: AssignmentExpression, fileSize?: AssignmentExpression): LoggingSection {
        if (name) {
            section.name = name;
        }
        if (description) {
            section.description = description;
        }
        if (group) {
            section.group = group;
        }
        if (path) {
            section.path = path;
        }
        if (alias) {
            section.alias;
        }
        if (fileSize) {
            section.fileSize = fileSize;
        }
        section.removceAllChildren();
        section.asChildren(section.name, section.description, section.group, section.path, section.alias, section.fileSize);
        return section;
    }

    function createOptionStatement(option: OptionSettingKeyword): OptionStatement {
        const statement = createNode(SyntaxKind.optionStat) as Mutable<OptionStatement>;
        statement.option = option;
        statement.optionFlag = OptionFlag.explicit;
        switch (option.kind) {
            case SyntaxKind.implicitKeyword:
                statement.optionFlag = OptionFlag.implicit;
                break;
            case SyntaxKind.explicitKeyword:
                statement.optionFlag = OptionFlag.explicit;
                break;
            case SyntaxKind.globalVariablesKeyword:
                statement.optionFlag = OptionFlag.globalVariables;
                break;
        }
        return statement;
    }

    function createJumpStatement(goto: KeywordToken<SyntaxKind.gotoKeyword>, location: Identifier | NumberLiteral): JumpStatement;
    function createJumpStatement(resume: ResumeStatement): JumpStatement;
    function createJumpStatement(exit: KeywordToken<SyntaxKind.exitKeyword>, precedure?: KeywordToken<PrecedureCanExit>): JumpStatement;
    function createJumpStatement(param1: KeywordToken<SyntaxKind.gotoKeyword | SyntaxKind.exitKeyword> | ResumeStatement, param2?: Identifier | NumberLiteral | KeywordToken<PrecedureCanExit>): JumpStatement {
        const jump = createStatement(SyntaxKind.jumpStat) as Mutable<JumpStatement>;

        if (param1.isToken(SyntaxKind.gotoKeyword)) {
            jump.goto = param1;
            if (param2 !== undefined) {
                if (param2.isNumberLiteral() || param2.isIdentifier()) {
                    jump.location = param2;
                }
            }
        } else if (param1.isToken(SyntaxKind.resumeKeyword)) {
            jump.resume = param1;
        } else if (param1.isToken(SyntaxKind.exitKeyword)) {
            jump.exit = param1;
            if (param2 && isPrecedureCanExitToken(param2)) {
                jump.exitPrecedure = param2;
            }
        }
        jump.asChildren(param1, param2);

        return jump;
    }

    function createResumeStatement(resume: KeywordToken<SyntaxKind.resumeKeyword>, next: KeywordToken<SyntaxKind.nextKeyword>): ResumeStatement {
        const statement = createStatement(SyntaxKind.resumeStat) as Mutable<ResumeStatement>;
        statement.resume = resume;
        statement.next = next;
        return statement;
    }

    function createPreprocessor<T extends Preprocessor>(flag: T["kind"]): Mutable<T> {
        const node = createNode(flag) as Mutable<T>;
        node._statementBrand = undefined;
        node._preprocessorBrand = undefined;
        return node;
    }

    function createPreprocessorDefineParameter(name: Identifier, isStringizer: boolean = false, isTokenPaster: boolean = false): PPDefineParameter {
        const parameter = createNode(SyntaxKind.ppDefineParameter) as Mutable<PPDefineParameter>;
        parameter.name = name;
        parameter.isStringizer = isStringizer;
        parameter.isTokenPaster = isTokenPaster;
        return parameter;
    }

    function updatePreprocessorDefineParameter(parameter: Mutable<PPDefineParameter>, isStringizer?: boolean, isTokenPaster?: boolean): PPDefineParameter {
        if (isStringizer !== undefined && isStringizer !== null) {
            parameter.isStringizer = isStringizer;
        }
        if (isTokenPaster !== undefined && isTokenPaster !== null) {
            parameter.isTokenPaster = isTokenPaster;
        }
        return parameter;
    }

    function createPreprocessorDefineStatement(name: Identifier, parameters: PPDefineParameter[] | undefined, definition: readonly Node[] | undefined): PPDefine {
        const define = createPreprocessor<PPDefine>(SyntaxKind.ppDefine);
        define.name = name;
        if (parameters) {
            define.parameters = createNodeArray(parameters);
        }
        if (definition) {
            define.definition = createNodeArray(definition);
        }
        define.asChildren(name);
        if (parameters) {
            define.asChildren(...parameters);
        }
        if (definition) {
            define.asChildren(...definition);
        }
        return define;
    }

    function createPreprocessorDefineStringizer(name: Identifier): PPDefineStringizer {
        const node = createNode(SyntaxKind.ppDefineStringizer) as Mutable<PPDefineStringizer>;
        node.name = name;
        node.asChildren(name);
        return node;
    }

    function createPreprocessorDefineTokenPaster(identifiers: readonly Identifier[]): PPDefineTokenPaster {
        const node = createNode(SyntaxKind.ppDefineTokenPaster) as Mutable<PPDefineTokenPaster>;
        node.identifiers = createNodeArray(identifiers);
        node.asChildren(...identifiers);
        return node;
    }

    function createPreprocessorUndefStatement(name: Identifier): PPUndef {
        const undef = createPreprocessor<PPUndef>(SyntaxKind.ppUndef);
        undef.name = name;
        undef.asChildren(name);
        return undef;
    }

    function createPreprocessorIncludeStatement(path: StringLiteral | Expression, resolvedPath: string, targetFile?: SourceFile): PPInclude {
        const include = createPreprocessor<PPInclude>(SyntaxKind.ppInclude);
        include.path = path;
        include.resolvedPath = resolvedPath;
        include.targetFile = targetFile;
        include.asChildren(path, targetFile);
        return include;
    }

    function createPreprocessorIfStatement(condition: Expression, then: Statement, elseStatement?: Statement): PPIf {
        const statement = createPreprocessor<PPIf>(SyntaxKind.ppIf);
        statement.condition = condition;
        statement.then = then;
        statement.else = elseStatement;
        statement.asChildren(condition, then, elseStatement);
        return statement;
    }

    function createPreprocessorIfDefStatement(macroName: Identifier, then: Statement, elseStatement?: Statement): PPIfDef {
        const statement = createPreprocessor<PPIfDef>(SyntaxKind.ppIfDef);
        statement.macroName = macroName;
        statement.then = then;
        statement.else = elseStatement;
        statement.asChildren(macroName, then, elseStatement);
        return statement;
    }

    function createPreprocessorIfNDefStatement(macroName: Identifier, then: Statement, elseStatement?: Statement): PPIfNDef {
        const statement = createPreprocessor<PPIfNDef>(SyntaxKind.ppIfNDef);
        statement.macroName = macroName;
        statement.then = then;
        statement.else = elseStatement;
        statement.asChildren(macroName, then, statement);
        return statement;
    }

    function createPreprocessorErrorStatement(text: string): PPError {
        const statement = createPreprocessor<PPError>(SyntaxKind.ppError);
        statement.text = text;
        return statement;
    }

    function createPreprocessorLineStatement(sequence: NumberLiteral, location?: StringLiteral): PPLine {
        const statement = createPreprocessor<PPLine>(SyntaxKind.ppLine);
        statement.sequence = sequence;
        statement.location = location;
        statement.asChildren(sequence, location);
        return statement;
    }

    // metadata

    function createMetadataAreaName(areaName: Identifier, language?: Expression, userContext?: Expression, labelType?: Expression): MDMAreaName {
        const node = createDeclaration(SyntaxKind.mdmAreaName, areaName) as Mutable<MDMAreaName>;
        node.areaName = areaName;
        node.language = language;
        node.context = userContext;
        node.labelType = labelType;
        node.userContextFlag = userContext ? (stringToUserContext(userContext.text) ?? MDMUserContextFlags.analysis) : MDMUserContextFlags.analysis;
        node.labelTypeFlag = labelType ? (stringToLabelType(labelType.text) ?? MDMLabelTypeFlags.label) : MDMLabelTypeFlags.label;
        node.asChildren(areaName, language, userContext, labelType);
        return node;
    }

    function updateMetadataAreaName(areaName: Mutable<MDMAreaName>, language?: Expression, userContext?: Expression, labelType?: Expression): MDMAreaName {
        if (language) {
            areaName.language = language;
        }
        if (userContext) {
            areaName.context = userContext;
        }
        if (labelType) {
            areaName.labelType = labelType;
        }
        areaName.removceAllChildren();
        areaName.asChildren(areaName.areaName, areaName.language, areaName.context, areaName.labelType);
        return areaName;
    }

    function createMetadataProperty(name: Identifier, value: Literal | Identifier): MDMProperty {
        const node = createDeclaration(SyntaxKind.mdmProperty, name) as Mutable<MDMProperty>;
        node.value = value;
        node.asChildren(name, value);
        return node;
    }

    function updateMetadataProperty(property: Mutable<MDMProperty>, name?: Identifier, value?: Literal | Identifier): MDMProperty {
        if (name) {
            property.name = name;
        }
        if (value) {
            property.value = value;
        }
        property.removceAllChildren();
        property.asChildren(property.name, property.value);
        return property;
    }

    function createMetadataTemplate(name: Identifier, value: Literal | Identifier): MDMTemplate {
        const node = createDeclaration(SyntaxKind.mdmTemplate, name) as Mutable<MDMTemplate>;
        node.value = value;
        node.asChildren(name, value);
        return node;
    }

    function createMetadataControlStyle(name: Identifier, properties: readonly [key: keyof(MDMControlStyleProperties), value: MDMProperty][]): MDMControlStyle {
        const node = createDeclaration(SyntaxKind.mdmControlStyle, name) as Mutable<MDMControlStyle>;
        // MetadataControlStyle的属性值允许重复配置，只保存第一个结果，忽略第一个之后的值
        for (const prop of properties) {
            if (node[prop[0]]) {
                continue;
            }
            node[prop[0]] = prop[1];
            node.asChildren(prop[1]);
        }
        return node;
    }

    function createMetadataFontStyle(name: Identifier, properties: readonly [key: keyof(MDMFontStyleProperties), value: MDMProperty][]): MDMFontStyle {
        const node = createDeclaration(SyntaxKind.mdmFontStyle, name) as Mutable<MDMFontStyle>;
        for (const prop of properties) {
            if (node[prop[0]]) {
                continue;
            }
            node[prop[0]] = prop[1];
            node.asChildren(prop[1]);
        }
        return node;
    }

    function createMetadataCellStyle(name: Identifier, properties: readonly [key: keyof(MDMCellStyleProperties), value: MDMProperty][]): MDMCellStyle {
        const node = createDeclaration(SyntaxKind.mdmCellStyle, name) as Mutable<MDMCellStyle>;
        for (const prop of properties) {
            if (node[prop[0]]) {
                continue;
            }
            node[prop[0]] = prop[1];
            node.asChildren(prop[1]);
        }
        return node;
    }

    function createMetadataStyle(name: Identifier, properties?: MDMStyleProperties, control?: MDMControlStyle, cell?: MDMCellStyle, font?: MDMFontStyle): MDMStyle {
        const node = new (metadataStyleConstructor || (metadataStyleConstructor = objectAllocator.getMetadataStyleConstructor()))(SyntaxKind.mdmStyle, -1, -1, name);
        if (properties) {
            node.set(properties);
        }
        node.set(control, cell, font);
        return node;
    }

    function createMetadataProperties(name: Identifier, properties?: readonly MDMProperty[], areaName?: Identifier): MDMProperties {
        const nodeArray = new (metadataPropertiesLikeConstructor || (metadataPropertiesLikeConstructor = objectAllocator.getMetadataPropertiesLikeConstructor()))(SyntaxKind.mdmProperties, -1, -1) as Node as Mutable<MDMProperties>;
        nodeArray.name = name;
        nodeArray.areaName = areaName;
        nodeArray._declarationBrand = undefined;
        nodeArray._statementBrand = undefined;
        nodeArray.asChildren(areaName);
        if (properties) {
            nodeArray.asChildren(...properties);
        }
        return nodeArray;
    }

    function createMetadataTemplates(name: Identifier, templates?: readonly MDMTemplate[], areaName?: Identifier): MDMTemplates {
        const nodeArray = new (metadataPropertiesLikeConstructor || (metadataPropertiesLikeConstructor = objectAllocator.getMetadataPropertiesLikeConstructor()))(SyntaxKind.mdmTemplates, -1, -1) as Node as Mutable<MDMTemplates>;
        nodeArray.name = name;
        nodeArray.areaName = areaName;
        nodeArray._declarationBrand = undefined;
        nodeArray._statementBrand = undefined;
        nodeArray.asChildren(areaName);
        if (templates) {
            nodeArray.asChildren(...templates);
        }
        return nodeArray;
    }

    function createMetadataLabel(label: StringLiteral | Token<SyntaxKind.minus>, areaName?: Identifier, areaNames?: MDMLabel[]): MDMLabel {
        const node = new (metadataArealLikeConstructor || (metadataArealLikeConstructor = objectAllocator.getMetadataArealLikeConstructor()))<MDMLabel>(SyntaxKind.mdmLabel, -1, -1) as Node as Mutable<MDMLabel>;
        node.label = label;
        node.areaName = areaName;
        if (areaNames) {
            node.addArealObject(areaNames);
        }
        node.asChildren(label, areaName);
        if (areaNames) {
            node.asChildren(...areaNames);
        }
        return node;
    }

    function updateMetadataLabel(labelObject: Mutable<MDMLabel>, label: StringLiteral | Token<SyntaxKind.minus>): MDMLabel {
        labelObject.removeChild(node => node.id === labelObject.label.id);
        labelObject.label = label;
        labelObject.asChildren(label);
        return labelObject;
    }

    function createMetadataDeclarationValueLike<F extends MDMDeclarationKind, V>(flag: F, name: Identifier, value: V): MDMDeclarationValueLike<F, V> {
        const node = createDeclaration(flag, name) as Mutable<MDMDeclarationValueLike<F, V>>;
        node.value = value;
        return node;
    }

    function createMetadataUsageType(name: Identifier, typeValue: StringLiteral, typeFlag: MDMUsageTypeFlags): MDMUsageType {
        const node = createMetadataDeclarationValueLike(SyntaxKind.mdmUsageType, name, typeValue) as Mutable<MDMUsageType>;
        node.typeFlag = typeFlag;
        return node;
    }

    function createMetadataHelperFields(name: Identifier, fields: readonly MDMField[]): MDMHelperFields {
        const node = createDeclaration(SyntaxKind.mdmHelperFields, name) as Mutable<MDMHelperFields>;
        node.fields = createNodeArray(fields);
        node.asChildren(...fields);
        return node;
    }

    function createMetadataCategoryExpression(name: Identifier, expression: StringLiteral, arg?: Token<SyntaxKind.mdmDeriveElementsKeyword | SyntaxKind.mdmNoDeriveElementsKeyword>): MDMCategoryExpression {
        const node = createMetadataDeclarationValueLike(SyntaxKind.mdmCategoryExpression, name, expression) as Mutable<MDMCategoryExpression>;
        if (arg && arg.kind === SyntaxKind.mdmDeriveElementsKeyword) {
            node.deriveElements = arg as Token<SyntaxKind.mdmDeriveElementsKeyword>;
        }
        else if (arg && arg.kind === SyntaxKind.mdmNoDeriveElementsKeyword) {
            node.noDeriveElements = arg as Token<SyntaxKind.mdmNoDeriveElementsKeyword>;
        }
        return node;
    }

    function createMetadataCategoryElementType(name: Identifier, elementType: Identifier | StringLiteral): MDMCategoryElementType {
        const node = createDeclaration(SyntaxKind.mdmElementType, name) as Mutable<MDMCategoryElementType>;
        node.elementType = elementType;
        node.elementTypeFlag = stringToMetadataElementTypeFlag(elementType.text.trim()) ?? MDMCategoryElementTypeFlags.analysisCategory;
        return node;
    }

    function createMetadataCategoryKeycode(name: Identifier, value: StringLiteral): MDMKeycode {
        return createMetadataDeclarationValueLike(SyntaxKind.mdmKeycode, name, value) as Mutable<MDMKeycode>;
    }

    function createMetadataCategoryFactor(name: Identifier, value: NumberLiteral | TrueLiteral | FalseLiteral | StringLiteral | NullLiteral): MDMFactor {
        return createMetadataDeclarationValueLike(SyntaxKind.mdmFactor, name, value) as Mutable<MDMFactor>;
    }

    function createMetadataSublist(name: Identifier,label: MDMLabel | undefined, sortOrder?: MDMSortOrder): MDMSublist {
        const node = createDeclaration(SyntaxKind.mdmSublist, name) as Mutable<MDMSublist>;
        node.label = label;
        if (sortOrder) {
            Object.assign(node, sortOrder);
        }
        return node;
    }

    function createMetadataCategoryMultiplier(name: Identifier, field?: StringLiteral | MDMField): MDMMultiplier {
        const node = createDeclaration(SyntaxKind.mdmMultiplier, name) as Mutable<MDMMultiplier>;
        node.field = field;
        node.asChildren(field);
        return node;
    }

    function createMetadataCategoryOther(name: Identifier, field?: StringLiteral | MDMField): MDMOther {
        const node = createDeclaration(SyntaxKind.mdmOther, name) as Mutable<MDMOther>;
        node.field = field;
        node.asChildren(field);
        return node;
    }

    function createMetadataCategory(name: Identifier, label?: MDMLabel, categories?: MDMCategory[]): MDMCategory;
    function createMetadataCategory(name: Identifier, label?: MDMLabel, list?: Identifier, sublist?: MDMSublist): MDMCategory;
    function createMetadataCategory(
        name: Identifier,
        label: MDMLabel | undefined,
        arg0?: Identifier | MDMCategory[],
        arg1?: MDMSublist
    ): MDMCategory {
        const node = createDeclaration(SyntaxKind.mdmCategory, name) as Mutable<MDMCategory>;
        node.label = label;
        // special | list | categories
        if (Array.isArray(arg0)) {
            node.categories = createMetadataCategories(arg0);
            node.asChildren(node.categories);
        }
        else {
            node.list = arg0;
        }
        node.sublist = arg1;
        return node;
    }

    function createMetadataCategories(categories: MDMCategory[], sortOrder?: MDMSortOrder, namespace?: Token<SyntaxKind.mdmNamespaceKeyword>): MDMCategories {
        const array = new (metadataCollectionConstructor || (metadataCollectionConstructor = objectAllocator.getMetadataCollectionConstructor()))<MDMCategory>(SyntaxKind.mdmCategories, -1, -1) as Node as Mutable<MDMCategories>;
        array.add(categories);
        if (sortOrder) {
            Object.assign(array, sortOrder);
        }
        array.namespace = namespace;
        return array;
    }

    function createMetadataValueRange(lowerBoundary?: NumberLiteral | StringLiteral, upperBoundary?: NumberLiteral | StringLiteral, step?: NumberLiteral, exclude?: Token<SyntaxKind.caret>, dotDot?: Token<SyntaxKind.dotDot>): MDMValueRange {
        const node = createNode(SyntaxKind.mdmValueRange) as Mutable<MDMValueRange>;
        node.lowerBoundary = lowerBoundary;
        node.upperBoundary = upperBoundary;
        node.step = step;
        node.exclude = exclude;
        node.dotDot = dotDot;
        return node;
    }

    function createMetadataClass(
        name: Identifier,
        label?: MDMLabel,
        types?: readonly MDMField[],
        fields?: readonly MDMField[],
        pages?: readonly MDMField[]): MDMClass {
        const node = createDeclaration(SyntaxKind.mdmClass, name) as Mutable<MDMClass>;
        node.label = label;
        node.types = createNodeArray(types);
        node.fields = createNodeArray(fields);
        node.pages = createNodeArray(pages);
        return node;
    }

    function createMetadataField(
        name: Identifier,
        label: MDMLabel | undefined,
        valueRange?: readonly MDMValueRange[],
        header?: MDMFieldDefinitionHeader,
        tail?: MDMFieldDefinitionTail,
        categories?: MDMCategory[],
        rowOrColumn?: Token<SyntaxKind.mdmRowKeyword | SyntaxKind.mdmColumnKeyword>,
        expand?: Token<SyntaxKind.mdmExpandKeyword>,
        grid?: Token<SyntaxKind.mdmGridKeyword>,
    ): MDMField {
        const node = createDeclaration(SyntaxKind.mdmField, name) as Mutable<MDMField>;
        node.label = label;
        node.fieldFlag = MDMFieldFlags.categorical;
        node.valueRange = createNodeArray(valueRange);
        node.asChildren(node.label);
        // header
        if (header) {
            Object.assign(node, header);
            let key: keyof MDMFieldDefinitionHeader;
            for (key in header) {
                if (header[key]) {
                    node.asChildren(header[key]);
                }
            }
        }
        // tail
        if (tail) {
            Object.assign(node, tail);
            let key: keyof MDMFieldDefinitionTail;
            for (key in tail) {
                let value = tail[key];
                if (!value) {
                    continue;
                }
                if (isNode(value)) {
                    node.asChildren(value);
                }
            }
        }
        // other
        node.categories = createMetadataCategories(categories ?? []);

        if (rowOrColumn) {
            if (rowOrColumn.kind === SyntaxKind.mdmRowKeyword) {
                node.row = rowOrColumn as Token<SyntaxKind.mdmRowKeyword>;
            }
            else {
                node.column = rowOrColumn as Token<SyntaxKind.mdmColumnKeyword>;
            }
        }

        node.expand = expand;
        node.grid = grid;

        return node;
    }

    function createMetadataDatabaseQuestions(
        baseField: MDMField,
        properties: MDMDbQuestionsProperties,
        dbType?: MDMFieldFlags,
        noExpand?: Token<SyntaxKind.mdmNoExpandKeyword>,
    ): MDMDbQuestions {
        const dbQuestions = baseField as Mutable<MDMDbQuestions>;
        dbQuestions.dbType = baseField.fieldFlag;
        dbQuestions.fieldFlag = MDMFieldFlags.databaseQuestions;
        dbQuestions.connectionString = properties.connectionString;
        dbQuestions.table = properties.table;
        dbQuestions.minAnswers = properties.minAnswers;
        dbQuestions.maxAnswers = properties.maxAnswers;
        dbQuestions.sqlFilter = properties.sqlFilter;
        dbQuestions.cacheTimeout = properties.cacheTimeout;
        dbQuestions.iteratorIDType = properties.iteratorIDType;
        dbQuestions.columns = properties.columns;
        dbQuestions.dbType = dbType;
        dbQuestions.noExpand = noExpand;
        dbQuestions._metadataDBBrand = undefined;
        return dbQuestions;
    }

    function createMetadataHierarchicalView(field: MDMField): MDMHierarchicalView {
        const node = createNode(SyntaxKind.mdmHierarchicalView) as Mutable<MDMHierarchicalView>;
        node.start = field.start;
        node.end = field.end;
        node.properties = field.properties;
        node.style = field.style;
        node.label = field.label;
        node.labelStyle = field.labelStyle;
        node.templates = field.templates;
        return node;
    }

    function createMetadataPage(baseField: MDMField, questionNames: readonly Identifier[]): MDMPage {
        const page = baseField as Mutable<MDMPage>;
        page.questionNames = createNodeArray(questionNames);
        page._metadataPageBrand = undefined;
        page.asChildren(...questionNames);
        return page;
    }

    function createMetadataDocument(
        language: LocalLanguage,
        context: MDMUserContextFlags,
        label: MDMLabelTypeFlags,
        types?: readonly MDMField[],
        fields?: readonly MDMField[],
        hdata?: MDMHierarchicalView,
        lcl?: readonly MDMAreaName[],
        preprocessor?: readonly Preprocessor[],
    ): MDMDocument {
        const document = createNode(SyntaxKind.mdmDocument) as Mutable<MDMDocument>;
        document.language = language;
        document.context = context;
        document.labelType = label;
        document.types = createNodeArray(types);
        document.fields = createNodeArray(fields);
        document.hdata = hdata;
        document.lcl = createNodeArray(lcl);
        document.preprocessor = createNodeArray(preprocessor);
        document.locals = createSymbolTable();
        return document;
    }

}

